diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/Documentation/magic-number.txt linux-2.6.0-test1-ac1/Documentation/magic-number.txt --- linux-2.6.0-test1/Documentation/magic-number.txt 2003-07-10 21:10:55.000000000 +0100 +++ linux-2.6.0-test1-ac1/Documentation/magic-number.txt 2003-07-14 14:51:16.000000000 +0100 @@ -51,6 +51,13 @@ 03 Nov 2002 +Updated the magic table to Linux 2.5.74. + + Fabian Frederick + + 09 Jul 2003 + + Magic Name Number Structure File =========================================================================== PG_MAGIC 'P' pg_{read,write}_hdr include/linux/pg.h @@ -62,10 +69,12 @@ HDLC_MAGIC 0x239e n_hdlc drivers/char/n_hdlc.c APM_BIOS_MAGIC 0x4101 apm_user arch/i386/kernel/apm.c CYCLADES_MAGIC 0x4359 cyclades_port include/linux/cyclades.h +DB_MAGIC 0x4442 fc_info drivers/net/iph5526_novram.c +DL_MAGIC 0x444d fc_info drivers/net/iph5526_novram.c FASYNC_MAGIC 0x4601 fasync_struct include/linux/fs.h +FF_MAGIC 0x4646 fc_info drivers/net/iph5526_novram.c ISICOM_MAGIC 0x4d54 isi_port include/linux/isicom.h -PTY_MAGIC 0x5001 (none at the moment) - drivers/char/pty.c +PTY_MAGIC 0x5001 drivers/char/pty.c PPP_MAGIC 0x5002 ppp include/linux/if_pppvar.h SERIAL_MAGIC 0x5301 async_struct include/linux/serial.h SSTATE_MAGIC 0x5302 serial_state include/linux/serial.h @@ -81,9 +90,9 @@ MGSLPC_MAGIC 0x5402 mgslpc_info drivers/char/pcmcia/synclink_cs.c TTY_LDISC_MAGIC 0x5403 tty_ldisc include/linux/tty_ldisc.h USB_SERIAL_MAGIC 0x6702 usb_serial drivers/usb/serial/usb-serial.h +FULL_DUPLEX_MAGIC 0x6969 drivers/net/tulip/de2104x.c USB_BLUETOOTH_MAGIC 0x6d02 usb_bluetooth drivers/usb/class/bluetty.c -RFCOMM_TTY_MAGIC 0x6d02 (note at the moment) - net/bluetooth/rfcomm/tty.c +RFCOMM_TTY_MAGIC 0x6d02 net/bluetooth/rfcomm/tty.c USB_SERIAL_PORT_MAGIC 0x7301 usb_serial_port drivers/usb/serial/usb-serial.h CG_MAGIC 0x00090255 ufs_cylinder_group include/linux/ufs_fs.h A2232_MAGIC 0x000a2232 gs_port drivers/char/ser_a2232.h @@ -91,6 +100,7 @@ RPORT_MAGIC 0x00525001 r_port drivers/char/rocket_int.h LSEMAGIC 0x05091998 lse drivers/fc4/fc.c GDTIOCTL_MAGIC 0x06030f07 gdth_iowr_str drivers/scsi/gdth_ioctl.h +RIEBL_MAGIC 0x09051990 drivers/net/atarilance.c RIO_MAGIC 0x12345678 gs_port drivers/char/rio/rio_linux.c SX_MAGIC 0x12345678 gs_port drivers/char/sx.h NBD_REQUEST_MAGIC 0x12560953 nbd_request include/linux/nbd.h @@ -120,6 +130,7 @@ GDA_MAGIC 0x58464552 gda include/asm-mips64/sn/gda.h RED_MAGIC1 0x5a2cf071 (any) mm/slab.c STL_PORTMAGIC 0x5a7182c9 stlport include/linux/stallion.h +EEPROM_MAGIC_VALUE 0X5ab478d2 lanai_dev drivers/atm/lanai.c HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state include/linux/hdlcdrv.h EPCA_MAGIC 0x5c6df104 channel include/linux/epca.h PCXX_MAGIC 0x5c6df104 channel drivers/char/pcxx.h @@ -127,9 +138,11 @@ I810_STATE_MAGIC 0x63657373 i810_state sound/oss/i810_audio.c TRIDENT_STATE_MAGIC 0x63657373 trient_state sound/oss/trident.c M3_CARD_MAGIC 0x646e6f50 m3_card sound/oss/maestro3.c +FW_HEADER_MAGIC 0x65726F66 fw_header drivers/atm/fore200e.h SLOT_MAGIC 0x67267321 slot drivers/hotplug/cpqphp.h SLOT_MAGIC 0x67267322 slot drivers/hotplug/acpiphp.h LO_MAGIC 0x68797548 nbd_device include/linux/nbd.h +OPROFILE_MAGIC 0x6f70726f super_block drivers/oprofile/oprofilefs.h M3_STATE_MAGIC 0x734d724d m3_state sound/oss/maestro3.c STL_PANELMAGIC 0x7ef621a1 stlpanel include/linux/stallion.h VMALLOC_MAGIC 0x87654320 snd_alloc_track sound/core/memory.c @@ -137,11 +150,15 @@ PWC_MAGIC 0x89DC10AB pwc_device drivers/usb/media/pwc.h NBD_REPLY_MAGIC 0x96744668 nbd_reply include/linux/nbd.h STL_BOARDMAGIC 0xa2267f52 stlbrd include/linux/stallion.h +ENI155_MAGIC 0xa54b872d midway_eprom drivers/atm/eni.h SCI_MAGIC 0xbabeface gs_port drivers/char/sh-sci.h CODA_MAGIC 0xC0DAC0DA coda_file_info include/linux/coda_fs_i.h +DPMEM_MAGIC 0xc0ffee11 gdt_pci_sram drivers/scsi/gdth.h STLI_PORTMAGIC 0xe671c7a1 stliport include/linux/istallion.h YAM_MAGIC 0xF10A7654 yam_port drivers/net/hamradio/yam.c CCB_MAGIC 0xf2691ad2 ccb drivers/scsi/ncr53c8xx.c +QUEUE_MAGIC_FREE 0xf7e1c9a3 queue_entry drivers/scsi/arm/queue.c +QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry drivers/scsi/arm/queue.c HTB_CMAGIC 0xFEFAFEF1 htb_class net/sched/sch_htb.c NMI_MAGIC 0x48414d4d455201 nmi_s include/asm-mips64/sn/nmi.h diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/ip2/i2lib.c linux-2.6.0-test1-ac1/drivers/char/ip2/i2lib.c --- linux-2.6.0-test1/drivers/char/ip2/i2lib.c 2003-07-10 21:15:35.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/ip2/i2lib.c 2003-07-14 14:47:09.000000000 +0100 @@ -1089,7 +1089,7 @@ // Move the data if ( user ) { - COPY_FROM_USER(rc, (char*)(DATA_OF(pInsert)), pSource, + rc = copy_from_user((char*)(DATA_OF(pInsert)), pSource, amountToMove ); } else { memcpy( (char*)(DATA_OF(pInsert)), pSource, amountToMove ); diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/ip2/i2os.h linux-2.6.0-test1-ac1/drivers/char/ip2/i2os.h --- linux-2.6.0-test1/drivers/char/ip2/i2os.h 2003-07-10 21:08:56.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/ip2/i2os.h 2003-07-14 14:47:09.000000000 +0100 @@ -19,8 +19,6 @@ #ifndef I2OS_H /* To prevent multiple includes */ #define I2OS_H 1 -#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq)) - //------------------------------------------------- // Required Includes //------------------------------------------------- @@ -46,22 +44,6 @@ // Interrupt control //-------------------------------------------- -#if LINUX_VERSION_CODE < 0x00020100 -typedef int spinlock_t; -#define spin_lock_init() -#define spin_lock(a) -#define spin_unlock(a) -#define spin_lock_irqsave(a,b) {save_flags((b));cli();} -#define spin_unlock_irqrestore(a,b) {restore_flags((b));} -#define write_lock_irqsave(a,b) spin_lock_irqsave(a,b) -#define write_unlock_irqrestore(a,b) spin_unlock_irqrestore(a,b) -#define read_lock_irqsave(a,b) spin_lock_irqsave(a,b) -#define read_unlock_irqrestore(a,b) spin_unlock_irqrestore(a,b) -#endif - -//#define SAVE_AND_DISABLE_INTS(a,b) spin_lock_irqsave(a,b) -//#define RESTORE_INTS(a,b) spin_unlock_irqrestore(a,b) - #define LOCK_INIT(a) rwlock_init(a) #define SAVE_AND_DISABLE_INTS(a,b) { \ diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/ip2.c linux-2.6.0-test1-ac1/drivers/char/ip2.c --- linux-2.6.0-test1/drivers/char/ip2.c 2003-07-10 21:09:03.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/ip2.c 2003-07-14 14:47:09.000000000 +0100 @@ -38,16 +38,14 @@ static int poll_only = 0; -# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) - MODULE_AUTHOR("Doug McNash"); - MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); - MODULE_PARM(irq,"1-"__MODULE_STRING(IP2_MAX_BOARDS) "i"); - MODULE_PARM_DESC(irq,"Interrupts for IntelliPort Cards"); - MODULE_PARM(io,"1-"__MODULE_STRING(IP2_MAX_BOARDS) "i"); - MODULE_PARM_DESC(io,"I/O ports for IntelliPort Cards"); - MODULE_PARM(poll_only,"1i"); - MODULE_PARM_DESC(poll_only,"Do not use card interrupts"); -# endif /* LINUX_VERSION */ +MODULE_AUTHOR("Doug McNash"); +MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); +MODULE_PARM(irq,"1-"__MODULE_STRING(IP2_MAX_BOARDS) "i"); +MODULE_PARM_DESC(irq,"Interrupts for IntelliPort Cards"); +MODULE_PARM(io,"1-"__MODULE_STRING(IP2_MAX_BOARDS) "i"); +MODULE_PARM_DESC(io,"I/O ports for IntelliPort Cards"); +MODULE_PARM(poll_only,"1i"); +MODULE_PARM_DESC(poll_only,"Do not use card interrupts"); //====================================================================== diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/ip2main.c linux-2.6.0-test1-ac1/drivers/char/ip2main.c --- linux-2.6.0-test1/drivers/char/ip2main.c 2003-07-10 21:15:44.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/ip2main.c 2003-07-14 14:47:09.000000000 +0100 @@ -83,7 +83,6 @@ /* Includes */ /************/ #include -// Uncomment the following if you want it compiled with modversions #include @@ -120,82 +119,11 @@ #include #include -#ifndef KERNEL_VERSION -#define KERNEL_VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq)) -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) -# include -# include -# include -#else -# include -#endif +#include +#include +#include -// These VERSION switches maybe inexact because I simply don't know -// when the various features appeared in the 2.1.XX kernels. -// They are good enough for 2.0 vs 2.2 and if you are fooling with -// the 2.1.XX stuff then it would be trivial for you to fix. -// Most of these macros were stolen from some other drivers -// so blame them. - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,4) -# define GET_USER(error,value,addr) error = get_user(value,addr) -# define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0 -# define PUT_USER(error,value,addr) error = put_user(value,addr) -# define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0 - -# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,5) -# include -# define pcibios_strerror(status) \ - printk( KERN_ERR "IP2: PCI error 0x%x \n", status ); -# endif - -#else /* 2.0.x and 2.1.x before 2.1.4 */ - -# define proc_register_dynamic(a,b) proc_register(a,b) - -# define GET_USER(error,value,addr) \ - do { \ - error = verify_area (VERIFY_READ, (void *) addr, sizeof (value)); \ - if (error == 0) \ - value = get_user(addr); \ - } while (0) - -# define COPY_FROM_USER(error,dest,src,size) \ - do { \ - error = verify_area (VERIFY_READ, (void *) src, size); \ - if (error == 0) \ - memcpy_fromfs (dest, src, size); \ - } while (0) - -# define PUT_USER(error,value,addr) \ - do { \ - error = verify_area (VERIFY_WRITE, (void *) addr, sizeof (value)); \ - if (error == 0) \ - put_user (value, addr); \ - } while (0) - -# define COPY_TO_USER(error,dest,src,size) \ - do { \ - error = verify_area (VERIFY_WRITE, (void *) dest, size); \ - if (error == 0) \ - memcpy_tofs (dest, src, size); \ - } while (0) - -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) -#define __init -#define __initfunc(a) a -#define __initdata -#define ioremap(a,b) vremap((a),(b)) -#define iounmap(a) vfree((a)) -#define SERIAL_TYPE_NORMAL 1 -#define schedule_timeout(a){current->timeout = jiffies + (a); schedule();} -#define signal_pending(a) ((a)->signal & ~(a)->blocked) -#define in_interrupt() intr_count -#endif +#include #include "./ip2/ip2types.h" #include "./ip2/ip2trace.h" @@ -276,11 +204,7 @@ static int get_serial_info(i2ChanStrPtr, struct serial_struct *); static int set_serial_info(i2ChanStrPtr, struct serial_struct *); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) -static int ip2_ipl_read(struct inode *, char *, size_t , loff_t *); -#else -static ssize_t ip2_ipl_read(struct file *, char *, size_t, loff_t *) ; -#endif +static ssize_t ip2_ipl_read(struct file *, char *, size_t, loff_t *); static ssize_t ip2_ipl_write(struct file *, const char *, size_t, loff_t *); static int ip2_ipl_ioctl(struct inode *, struct file *, UINT, ULONG); static int ip2_ipl_open(struct inode *, struct file *); @@ -354,9 +278,6 @@ #define DBG_CNT(s) #endif -#define MIN(a,b) ( ( (a) < (b) ) ? (a) : (b) ) -#define MAX(a,b) ( ( (a) > (b) ) ? (a) : (b) ) - /********/ /* Code */ /********/ @@ -366,12 +287,9 @@ #include "./ip2/i2lib.c" /* High level interface services */ /* Configuration area for modprobe */ -#ifdef MODULE -# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) - MODULE_AUTHOR("Doug McNash"); - MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); -# endif /* LINUX_VERSION */ -#endif /* MODULE */ + +MODULE_AUTHOR("Doug McNash"); +MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); static int poll_only = 0; @@ -660,53 +578,6 @@ break; case PCI: #ifdef CONFIG_PCI -#if (LINUX_VERSION_CODE < 0x020163) /* 2.1.99 */ - if (pcibios_present()) { - unsigned char pci_bus, pci_devfn; - int Pci_index = 0; - status = pcibios_find_device(PCI_VENDOR_ID_COMPUTONE, - PCI_DEVICE_ID_COMPUTONE_IP2EX, Pci_index, - &pci_bus, &pci_devfn); - if (status == 0) { - unsigned int addr; - unsigned char pci_irq; - - ip2config.type[i] = PCI; - /* - * Update Pci_index, so that the next time we go - * searching for a PCI board we find a different - * one. - */ - ++Pci_index; - - pcibios_read_config_dword(pci_bus, pci_devfn, - PCI_BASE_ADDRESS_1, &addr); - if ( addr & 1 ) { - ip2config.addr[i]=(USHORT)(addr&0xfffe); - } else { - printk( KERN_ERR "IP2: PCI I/O address error\n"); - } - pcibios_read_config_byte(pci_bus, pci_devfn, - PCI_INTERRUPT_LINE, &pci_irq); - -// If the PCI BIOS assigned it, lets try and use it. If we -// can't acquire it or it screws up, deal with it then. - -// if (!is_valid_irq(pci_irq)) { -// printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq); -// pci_irq = 0; -// } - ip2config.irq[i] = pci_irq; - } else { // ann error - ip2config.addr[i] = 0; - if (status == PCIBIOS_DEVICE_NOT_FOUND) { - printk( KERN_ERR "IP2: PCI board %d not found\n", i ); - } else { - pcibios_strerror(status); - } - } - } -#else /* LINUX_VERSION_CODE > 2.1.99 */ { struct pci_dev *pci_dev_i = NULL; pci_dev_i = pci_find_device(PCI_VENDOR_ID_COMPUTONE, @@ -739,11 +610,10 @@ if (status == PCIBIOS_DEVICE_NOT_FOUND) { printk( KERN_ERR "IP2: PCI board %d not found\n", i ); } else { - pcibios_strerror(status); + printk( KERN_ERR "IP2: PCI error 0x%x \n", status ); } } } -#endif /* ! 2_0_X */ #else printk( KERN_ERR "IP2: PCI card specified but PCI support not\n"); printk( KERN_ERR "IP2: configured in this kernel.\n"); @@ -2193,7 +2063,7 @@ ip2trace (CHANN, ITRC_IOCTL, 6, 1, rc ); - PUT_USER(rc,C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); + rc = put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); if (rc) return rc; break; @@ -2202,7 +2072,7 @@ ip2trace (CHANN, ITRC_IOCTL, 7, 1, rc ); - GET_USER(rc,arg,(unsigned long *) arg); + rc = get_user(arg,(unsigned long *) arg); if (rc) return rc; tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) @@ -2243,7 +2113,7 @@ return -EINTR; } #endif - PUT_USER(rc, + rc = put_user( ((pCh->dataSetOut & I2_RTS) ? TIOCM_RTS : 0) | ((pCh->dataSetOut & I2_DTR) ? TIOCM_DTR : 0) | ((pCh->dataSetIn & I2_DCD) ? TIOCM_CAR : 0) @@ -2333,17 +2203,17 @@ cnow = pCh->icount; restore_flags(flags); p_cuser = (struct serial_icounter_struct *) arg; - PUT_USER(rc,cnow.cts, &p_cuser->cts); - PUT_USER(rc,cnow.dsr, &p_cuser->dsr); - PUT_USER(rc,cnow.rng, &p_cuser->rng); - PUT_USER(rc,cnow.dcd, &p_cuser->dcd); - PUT_USER(rc,cnow.rx, &p_cuser->rx); - PUT_USER(rc,cnow.tx, &p_cuser->tx); - PUT_USER(rc,cnow.frame, &p_cuser->frame); - PUT_USER(rc,cnow.overrun, &p_cuser->overrun); - PUT_USER(rc,cnow.parity, &p_cuser->parity); - PUT_USER(rc,cnow.brk, &p_cuser->brk); - PUT_USER(rc,cnow.buf_overrun, &p_cuser->buf_overrun); + rc = put_user(cnow.cts, &p_cuser->cts); + rc = put_user(cnow.dsr, &p_cuser->dsr); + rc = put_user(cnow.rng, &p_cuser->rng); + rc = put_user(cnow.dcd, &p_cuser->dcd); + rc = put_user(cnow.rx, &p_cuser->rx); + rc = put_user(cnow.tx, &p_cuser->tx); + rc = put_user(cnow.frame, &p_cuser->frame); + rc = put_user(cnow.overrun, &p_cuser->overrun); + rc = put_user(cnow.parity, &p_cuser->parity); + rc = put_user(cnow.brk, &p_cuser->brk); + rc = put_user(cnow.buf_overrun, &p_cuser->buf_overrun); break; /* @@ -2387,7 +2257,7 @@ int rc; unsigned int arg; - GET_USER(rc,arg,value); + rc = get_user(arg,value); if (rc) return rc; switch(cmd) { @@ -2469,7 +2339,7 @@ tmp.close_delay = pCh->ClosingDelay; tmp.closing_wait = pCh->ClosingWaitTime; tmp.custom_divisor = pCh->BaudDivisor; - COPY_TO_USER(rc,retinfo,&tmp,sizeof(*retinfo)); + rc = copy_to_user(retinfo,&tmp,sizeof(*retinfo)); return rc; } @@ -2489,15 +2359,15 @@ { struct serial_struct ns; int old_flags, old_baud_divisor; - int rc = 0; if ( !new_info ) { return -EFAULT; } - COPY_FROM_USER(rc, &ns, new_info, sizeof (ns) ); - if (rc) { - return rc; + + if (copy_from_user(&ns, new_info, sizeof (ns))) { + return -EFAULT; } + /* * We don't allow setserial to change IRQ, board address, type or baud * base. Also line nunber as such is meaningless but we use it for our @@ -2537,7 +2407,7 @@ set_params( pCh, NULL ); } - return rc; + return 0; } /******************************************************************************/ @@ -2860,16 +2730,10 @@ /******************************************************************************/ static -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) -int -ip2_ipl_read(struct inode *pInode, char *pData, size_t count, loff_t *off ) - unsigned int minor = minor( pInode->i_rdev ); -#else ssize_t ip2_ipl_read(struct file *pFile, char *pData, size_t count, loff_t *off ) { unsigned int minor = minor( pFile->f_dentry->d_inode->i_rdev ); -#endif int rc = 0; #ifdef IP2DEBUG_IPL @@ -2904,7 +2768,7 @@ { #ifdef DEBUG_FIFO int rc; - COPY_TO_USER(rc, pData, DBGBuf, count); + rc = copy_to_user(pData, DBGBuf, count); printk(KERN_DEBUG "Last index %d\n", I ); @@ -2925,10 +2789,10 @@ if ( count < (sizeof(int) * 6) ) { return -EIO; } - PUT_USER(rc, tracewrap, pIndex ); - PUT_USER(rc, TRACEMAX, ++pIndex ); - PUT_USER(rc, tracestrip, ++pIndex ); - PUT_USER(rc, tracestuff, ++pIndex ); + rc = put_user(tracewrap, pIndex ); + rc = put_user(TRACEMAX, ++pIndex ); + rc = put_user(tracestrip, ++pIndex ); + rc = put_user(tracestuff, ++pIndex ); pData += sizeof(int) * 6; count -= sizeof(int) * 6; @@ -2941,7 +2805,7 @@ } chunk = TRACEMAX - tracestrip; if ( dumpcount > chunk ) { - COPY_TO_USER(rc, pData, &tracebuf[tracestrip], + rc = copy_to_user(pData, &tracebuf[tracestrip], chunk * sizeof(tracebuf[0]) ); pData += chunk * sizeof(tracebuf[0]); tracestrip = 0; @@ -2949,13 +2813,13 @@ } else { chunk = dumpcount; } - COPY_TO_USER(rc, pData, &tracebuf[tracestrip], + rc = copy_to_user(pData, &tracebuf[tracestrip], chunk * sizeof(tracebuf[0]) ); tracestrip += chunk; tracewrap = 0; - PUT_USER(rc, tracestrip, ++pIndex ); - PUT_USER(rc, tracestuff, ++pIndex ); + rc = put_user(tracestrip, ++pIndex ); + rc = put_user(tracestuff, ++pIndex ); return dumpcount; #else @@ -3019,15 +2883,15 @@ case 13: switch ( cmd ) { case 64: /* Driver - ip2stat */ - PUT_USER(rc, ip2_tty_driver->refcount, pIndex++ ); - PUT_USER(rc, irq_counter, pIndex++ ); - PUT_USER(rc, bh_counter, pIndex++ ); + rc = put_user(ip2_tty_driver->refcount, pIndex++ ); + rc = put_user(irq_counter, pIndex++ ); + rc = put_user(bh_counter, pIndex++ ); break; case 65: /* Board - ip2stat */ if ( pB ) { - COPY_TO_USER(rc, (char*)arg, (char*)pB, sizeof(i2eBordStr) ); - PUT_USER(rc, INB(pB->i2eStatus), + rc = copy_to_user((char*)arg, (char*)pB, sizeof(i2eBordStr) ); + rc = put_user(INB(pB->i2eStatus), (ULONG*)(arg + (ULONG)(&pB->i2eStatus) - (ULONG)pB ) ); } else { rc = -ENODEV; @@ -3039,7 +2903,7 @@ pCh = DevTable[cmd]; if ( pCh ) { - COPY_TO_USER(rc, (char*)arg, (char*)pCh, sizeof(i2ChanStr) ); + rc = copy_to_user((char*)arg, (char*)pCh, sizeof(i2ChanStr) ); } else { rc = -ENODEV; } @@ -3054,60 +2918,60 @@ break; case 3: // Trace device if ( cmd == 1 ) { - PUT_USER(rc, iiSendPendingMail, pIndex++ ); - PUT_USER(rc, i2InitChannels, pIndex++ ); - PUT_USER(rc, i2QueueNeeds, pIndex++ ); - PUT_USER(rc, i2QueueCommands, pIndex++ ); - PUT_USER(rc, i2GetStatus, pIndex++ ); - PUT_USER(rc, i2Input, pIndex++ ); - PUT_USER(rc, i2InputFlush, pIndex++ ); - PUT_USER(rc, i2Output, pIndex++ ); - PUT_USER(rc, i2FlushOutput, pIndex++ ); - PUT_USER(rc, i2DrainWakeup, pIndex++ ); - PUT_USER(rc, i2DrainOutput, pIndex++ ); - PUT_USER(rc, i2OutputFree, pIndex++ ); - PUT_USER(rc, i2StripFifo, pIndex++ ); - PUT_USER(rc, i2StuffFifoBypass, pIndex++ ); - PUT_USER(rc, i2StuffFifoFlow, pIndex++ ); - PUT_USER(rc, i2StuffFifoInline, pIndex++ ); - PUT_USER(rc, i2ServiceBoard, pIndex++ ); - PUT_USER(rc, serviceOutgoingFifo, pIndex++ ); - // PUT_USER(rc, ip2_init, pIndex++ ); - PUT_USER(rc, ip2_init_board, pIndex++ ); - PUT_USER(rc, find_eisa_board, pIndex++ ); - PUT_USER(rc, set_irq, pIndex++ ); - PUT_USER(rc, ip2_interrupt, pIndex++ ); - PUT_USER(rc, ip2_poll, pIndex++ ); - PUT_USER(rc, service_all_boards, pIndex++ ); - PUT_USER(rc, do_input, pIndex++ ); - PUT_USER(rc, do_status, pIndex++ ); + rc = put_user(iiSendPendingMail, pIndex++ ); + rc = put_user(i2InitChannels, pIndex++ ); + rc = put_user(i2QueueNeeds, pIndex++ ); + rc = put_user(i2QueueCommands, pIndex++ ); + rc = put_user(i2GetStatus, pIndex++ ); + rc = put_user(i2Input, pIndex++ ); + rc = put_user(i2InputFlush, pIndex++ ); + rc = put_user(i2Output, pIndex++ ); + rc = put_user(i2FlushOutput, pIndex++ ); + rc = put_user(i2DrainWakeup, pIndex++ ); + rc = put_user(i2DrainOutput, pIndex++ ); + rc = put_user(i2OutputFree, pIndex++ ); + rc = put_user(i2StripFifo, pIndex++ ); + rc = put_user(i2StuffFifoBypass, pIndex++ ); + rc = put_user(i2StuffFifoFlow, pIndex++ ); + rc = put_user(i2StuffFifoInline, pIndex++ ); + rc = put_user(i2ServiceBoard, pIndex++ ); + rc = put_user(serviceOutgoingFifo, pIndex++ ); + // rc = put_user(ip2_init, pIndex++ ); + rc = put_user(ip2_init_board, pIndex++ ); + rc = put_user(find_eisa_board, pIndex++ ); + rc = put_user(set_irq, pIndex++ ); + rc = put_user(ip2_interrupt, pIndex++ ); + rc = put_user(ip2_poll, pIndex++ ); + rc = put_user(service_all_boards, pIndex++ ); + rc = put_user(do_input, pIndex++ ); + rc = put_user(do_status, pIndex++ ); #ifndef IP2DEBUG_OPEN - PUT_USER(rc, 0, pIndex++ ); + rc = put_user(0, pIndex++ ); #else - PUT_USER(rc, open_sanity_check, pIndex++ ); + rc = put_user(open_sanity_check, pIndex++ ); #endif - PUT_USER(rc, ip2_open, pIndex++ ); - PUT_USER(rc, ip2_close, pIndex++ ); - PUT_USER(rc, ip2_hangup, pIndex++ ); - PUT_USER(rc, ip2_write, pIndex++ ); - PUT_USER(rc, ip2_putchar, pIndex++ ); - PUT_USER(rc, ip2_flush_chars, pIndex++ ); - PUT_USER(rc, ip2_write_room, pIndex++ ); - PUT_USER(rc, ip2_chars_in_buf, pIndex++ ); - PUT_USER(rc, ip2_flush_buffer, pIndex++ ); - - //PUT_USER(rc, ip2_wait_until_sent, pIndex++ ); - PUT_USER(rc, 0, pIndex++ ); - - PUT_USER(rc, ip2_throttle, pIndex++ ); - PUT_USER(rc, ip2_unthrottle, pIndex++ ); - PUT_USER(rc, ip2_ioctl, pIndex++ ); - PUT_USER(rc, set_modem_info, pIndex++ ); - PUT_USER(rc, get_serial_info, pIndex++ ); - PUT_USER(rc, set_serial_info, pIndex++ ); - PUT_USER(rc, ip2_set_termios, pIndex++ ); - PUT_USER(rc, ip2_set_line_discipline, pIndex++ ); - PUT_USER(rc, set_params, pIndex++ ); + rc = put_user(ip2_open, pIndex++ ); + rc = put_user(ip2_close, pIndex++ ); + rc = put_user(ip2_hangup, pIndex++ ); + rc = put_user(ip2_write, pIndex++ ); + rc = put_user(ip2_putchar, pIndex++ ); + rc = put_user(ip2_flush_chars, pIndex++ ); + rc = put_user(ip2_write_room, pIndex++ ); + rc = put_user(ip2_chars_in_buf, pIndex++ ); + rc = put_user(ip2_flush_buffer, pIndex++ ); + + //rc = put_user(ip2_wait_until_sent, pIndex++ ); + rc = put_user(0, pIndex++ ); + + rc = put_user(ip2_throttle, pIndex++ ); + rc = put_user(ip2_unthrottle, pIndex++ ); + rc = put_user(ip2_ioctl, pIndex++ ); + rc = put_user(set_modem_info, pIndex++ ); + rc = put_user(get_serial_info, pIndex++ ); + rc = put_user(set_serial_info, pIndex++ ); + rc = put_user(ip2_set_termios, pIndex++ ); + rc = put_user(ip2_set_line_discipline, pIndex++ ); + rc = put_user(set_params, pIndex++ ); } else { rc = -EINVAL; } diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/pcxx.c linux-2.6.0-test1-ac1/drivers/char/pcxx.c --- linux-2.6.0-test1/drivers/char/pcxx.c 2003-07-10 21:11:01.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/pcxx.c 2003-07-14 14:47:34.000000000 +0100 @@ -110,7 +110,6 @@ static int altpin[] = {0, 0, 0, 0}; static int numports[] = {0, 0, 0, 0}; -# if (LINUX_VERSION_CODE > 0x020111) MODULE_AUTHOR("Bernhard Kaindl"); MODULE_DESCRIPTION("Digiboard PC/X{i,e,eve} driver"); MODULE_LICENSE("GPL"); @@ -121,7 +120,6 @@ MODULE_PARM(memsize, "1-4i"); MODULE_PARM(altpin, "1-4i"); MODULE_PARM(numports, "1-4i"); -# endif #endif MODULE diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/watchdog/acquirewdt.c linux-2.6.0-test1-ac1/drivers/char/watchdog/acquirewdt.c --- linux-2.6.0-test1/drivers/char/watchdog/acquirewdt.c 2003-07-10 21:11:36.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/watchdog/acquirewdt.c 2003-07-14 14:48:04.000000000 +0100 @@ -143,7 +143,7 @@ return -EBUSY; } if (nowayout) - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); /* Activate */ acq_is_open=1; diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/watchdog/i810-tco.c linux-2.6.0-test1-ac1/drivers/char/watchdog/i810-tco.c --- linux-2.6.0-test1/drivers/char/watchdog/i810-tco.c 2003-07-10 21:04:08.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/watchdog/i810-tco.c 2003-07-14 14:58:02.000000000 +0100 @@ -25,7 +25,8 @@ * 82801AA & 82801AB chip : document number 290655-003, 290677-004, * 82801BA & 82801BAM chip : document number 290687-002, 298242-005, * 82801CA & 82801CAM chip : document number 290716-001, 290718-001, - * 82801DB & 82801E chip : document number 290744-001, 273599-001 + * 82801DB & 82801E chip : document number 290744-001, 273599-001, + * 82801EB & 82801ER chip : document number 252516-001 * * 20000710 Nils Faerber * Initial Version 0.01 @@ -42,9 +43,11 @@ * clean up ioctls (WDIOC_GETSTATUS, WDIOC_GETBOOTSTATUS and * WDIOC_SETOPTIONS), made i810tco_getdevice __init, * removed boot_status, removed tco_timer_read, - * added support for 82801DB and 82801E chipset, general cleanup. + * added support for 82801DB and 82801E chipset, + * added support for 82801EB and 8280ER chipset, + * general cleanup. */ - + #include #include #include @@ -164,7 +167,7 @@ * Reload (trigger) the timer. Lock is needed so we don't reload it during * a reprogramming event */ - + static void tco_timer_reload (void) { spin_lock(&tco_lock); @@ -307,6 +310,7 @@ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, PCI_ANY_ID, PCI_ANY_ID, }, { 0, }, }; MODULE_DEVICE_TABLE (pci, i810tco_pci_tbl); diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/watchdog/i810-tco.h linux-2.6.0-test1-ac1/drivers/char/watchdog/i810-tco.h --- linux-2.6.0-test1/drivers/char/watchdog/i810-tco.h 2003-07-10 21:12:20.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/watchdog/i810-tco.h 2003-07-14 14:58:09.000000000 +0100 @@ -1,5 +1,5 @@ /* - * i810-tco 0.05: TCO timer driver for i8xx chipsets + * i810-tco: TCO timer driver for i8xx chipsets * * (c) Copyright 2000 kernel concepts , All Rights Reserved. * http://www.kernelconcepts.de @@ -8,7 +8,7 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. - * + * * Neither kernel concepts nor Nils Faerber admit liability nor provide * warranty for any of this software. This material is provided * "AS-IS" and at no charge. diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/watchdog/ib700wdt.c linux-2.6.0-test1-ac1/drivers/char/watchdog/ib700wdt.c --- linux-2.6.0-test1/drivers/char/watchdog/ib700wdt.c 2003-07-10 21:14:20.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/watchdog/ib700wdt.c 2003-07-14 14:48:04.000000000 +0100 @@ -230,7 +230,7 @@ return -EBUSY; } if (nowayout) - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); /* Activate */ ibwdt_is_open = 1; diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/watchdog/indydog.c linux-2.6.0-test1-ac1/drivers/char/watchdog/indydog.c --- linux-2.6.0-test1/drivers/char/watchdog/indydog.c 2003-07-10 21:14:50.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/watchdog/indydog.c 2003-07-14 14:48:04.000000000 +0100 @@ -54,7 +54,7 @@ return -EBUSY; if (nowayout) - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); /* * Activate timer diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/watchdog/machzwd.c linux-2.6.0-test1-ac1/drivers/char/watchdog/machzwd.c --- linux-2.6.0-test1/drivers/char/watchdog/machzwd.c 2003-07-10 21:05:39.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/watchdog/machzwd.c 2003-07-14 14:48:04.000000000 +0100 @@ -392,7 +392,7 @@ } if (nowayout) - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); zf_is_open = 1; diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/watchdog/pcwd.c linux-2.6.0-test1-ac1/drivers/char/watchdog/pcwd.c --- linux-2.6.0-test1/drivers/char/watchdog/pcwd.c 2003-07-10 21:04:53.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/watchdog/pcwd.c 2003-07-14 14:48:04.000000000 +0100 @@ -431,7 +431,7 @@ atomic_inc( &open_allowed ); return -EBUSY; } - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); /* Enable the port */ if (revision == PCWD_REVISION_C) { spin_lock(&io_lock); diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/watchdog/sbc60xxwdt.c linux-2.6.0-test1-ac1/drivers/char/watchdog/sbc60xxwdt.c --- linux-2.6.0-test1/drivers/char/watchdog/sbc60xxwdt.c 2003-07-10 21:12:20.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/watchdog/sbc60xxwdt.c 2003-07-14 14:48:04.000000000 +0100 @@ -207,9 +207,8 @@ /* Just in case we're already talking to someone... */ if(wdt_is_open) return -EBUSY; - if (nowayout) { - MOD_INC_USE_COUNT; - } + if (nowayout) + __module_get(THIS_MODULE); /* Good, fire up the show */ wdt_is_open = 1; wdt_startup(); diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/watchdog/sc520_wdt.c linux-2.6.0-test1-ac1/drivers/char/watchdog/sc520_wdt.c --- linux-2.6.0-test1/drivers/char/watchdog/sc520_wdt.c 2003-07-10 21:09:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/watchdog/sc520_wdt.c 2003-07-14 14:48:04.000000000 +0100 @@ -231,7 +231,7 @@ /* Good, fire up the show */ wdt_startup(); if (nowayout) - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); return 0; default: diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/watchdog/shwdt.c linux-2.6.0-test1-ac1/drivers/char/watchdog/shwdt.c --- linux-2.6.0-test1/drivers/char/watchdog/shwdt.c 2003-07-10 21:06:57.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/watchdog/shwdt.c 2003-07-14 14:48:04.000000000 +0100 @@ -189,9 +189,8 @@ if (test_and_set_bit(0, &sh_is_open)) return -EBUSY; - if (nowayout) { - MOD_INC_USE_COUNT; - } + if (nowayout) + __module_get(THIS_MODULE); sh_wdt_start(); diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/watchdog/softdog.c linux-2.6.0-test1-ac1/drivers/char/watchdog/softdog.c --- linux-2.6.0-test1/drivers/char/watchdog/softdog.c 2003-07-10 21:08:26.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/watchdog/softdog.c 2003-07-14 14:48:04.000000000 +0100 @@ -104,9 +104,8 @@ { if(test_and_set_bit(0, &timer_alive)) return -EBUSY; - if (nowayout) { - MOD_INC_USE_COUNT; - } + if (nowayout) + __module_get(THIS_MODULE); /* * Activate timer */ diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/watchdog/wdt977.c linux-2.6.0-test1-ac1/drivers/char/watchdog/wdt977.c --- linux-2.6.0-test1/drivers/char/watchdog/wdt977.c 2003-07-10 21:13:04.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/watchdog/wdt977.c 2003-07-14 14:48:04.000000000 +0100 @@ -99,7 +99,7 @@ if (nowayout) { - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); /* do not permit disabling the watchdog by writing 0 to reg. 0xF2 */ if (!timeoutM) timeoutM = DEFAULT_TIMEOUT; diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/char/watchdog/wdt_pci.c linux-2.6.0-test1-ac1/drivers/char/watchdog/wdt_pci.c --- linux-2.6.0-test1/drivers/char/watchdog/wdt_pci.c 2003-07-10 21:12:08.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/char/watchdog/wdt_pci.c 2003-07-14 14:48:04.000000000 +0100 @@ -367,7 +367,7 @@ return -EBUSY; if (nowayout) { - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); } /* * Activate diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/ide/pci/cs5520.c linux-2.6.0-test1-ac1/drivers/ide/pci/cs5520.c --- linux-2.6.0-test1/drivers/ide/pci/cs5520.c 2003-07-10 21:04:54.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/ide/pci/cs5520.c 2003-07-14 16:57:44.000000000 +0100 @@ -292,7 +292,6 @@ probe_hwif_init(&ide_hwifs[index.b.low]); if((index.b.high & 0xf0) != 0xf0) probe_hwif_init(&ide_hwifs[index.b.high]); - MOD_INC_USE_COUNT; return 0; } diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/isdn/hisax/sedlbauer.c linux-2.6.0-test1-ac1/drivers/isdn/hisax/sedlbauer.c --- linux-2.6.0-test1/drivers/isdn/hisax/sedlbauer.c 2003-07-10 21:15:01.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/isdn/hisax/sedlbauer.c 2003-07-11 13:04:25.000000000 +0100 @@ -760,7 +760,7 @@ printk(KERN_ERR "Sedlbauer PnP:some resources are missing %ld/%lx\n", pnp_irq(pd, 0), pnp_port_start(pd, 0)); pnp_device_detach(pd); - goto err; + return 0; } card->para[1] = pnp_port_start(pd, 0); card->para[0] = pnp_irq(pd, 0); @@ -777,7 +777,7 @@ } } else { printk(KERN_ERR "Sedlbauer PnP: PnP error card found, no device\n"); - goto err; + return 0; } } pdev++; diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/md/dm.c linux-2.6.0-test1-ac1/drivers/md/dm.c --- linux-2.6.0-test1/drivers/md/dm.c 2003-07-10 21:12:58.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/md/dm.c 2003-07-14 14:50:50.000000000 +0100 @@ -63,6 +63,12 @@ * io objects are allocated from here. */ mempool_t *io_pool; + + /* + * Event handling. + */ + uint32_t event_nr; + wait_queue_head_t eventq; }; #define MIN_IOS 256 @@ -510,6 +516,11 @@ down_read(&md->lock); } + if (!md->map) { + bio_io_error(bio, bio->bi_size); + return 0; + } + __split_bio(md, bio); up_read(&md->lock); return 0; @@ -619,6 +630,8 @@ atomic_set(&md->pending, 0); init_waitqueue_head(&md->wait); + init_waitqueue_head(&md->eventq); + return md; } @@ -634,6 +647,16 @@ /* * Bind a table to the device. */ +static void event_callback(void *context) +{ + struct mapped_device *md = (struct mapped_device *) context; + + down_write(&md->lock); + md->event_nr++; + wake_up_interruptible(&md->eventq); + up_write(&md->lock); +} + static int __bind(struct mapped_device *md, struct dm_table *t) { request_queue_t *q = &md->queue; @@ -645,6 +668,8 @@ if (size == 0) return 0; + dm_table_event_callback(md->map, event_callback, md); + dm_table_get(t); dm_table_set_restrictions(t, q); return 0; @@ -652,6 +677,10 @@ static void __unbind(struct mapped_device *md) { + if (!md->map) + return; + + dm_table_event_callback(md->map, NULL, NULL); dm_table_put(md->map); md->map = NULL; set_capacity(md->disk, 0); @@ -661,35 +690,26 @@ * Constructor for a new device. */ static int create_aux(unsigned int minor, int persistent, - struct dm_table *table, struct mapped_device **result) + struct mapped_device **result) { - int r; struct mapped_device *md; md = alloc_dev(minor, persistent); if (!md) return -ENXIO; - r = __bind(md, table); - if (r) { - free_dev(md); - return r; - } - dm_table_resume_targets(md->map); - *result = md; return 0; } -int dm_create(struct dm_table *table, struct mapped_device **result) +int dm_create(struct mapped_device **result) { - return create_aux(0, 0, table, result); + return create_aux(0, 0, result); } -int dm_create_with_minor(unsigned int minor, - struct dm_table *table, struct mapped_device **result) +int dm_create_with_minor(unsigned int minor, struct mapped_device **result) { - return create_aux(minor, 1, table, result); + return create_aux(minor, 1, result); } void dm_get(struct mapped_device *md) @@ -700,7 +720,7 @@ void dm_put(struct mapped_device *md) { if (atomic_dec_and_test(&md->holders)) { - if (!test_bit(DMF_SUSPENDED, &md->flags)) + if (!test_bit(DMF_SUSPENDED, &md->flags) && md->map) dm_table_suspend_targets(md->map); __unbind(md); free_dev(md); @@ -790,7 +810,8 @@ down_write(&md->lock); remove_wait_queue(&md->wait, &wait); set_bit(DMF_SUSPENDED, &md->flags); - dm_table_suspend_targets(md->map); + if (md->map) + dm_table_suspend_targets(md->map); up_write(&md->lock); return 0; @@ -801,7 +822,8 @@ struct deferred_io *def; down_write(&md->lock); - if (!test_bit(DMF_SUSPENDED, &md->flags) || + if (!md->map || + !test_bit(DMF_SUSPENDED, &md->flags) || !dm_table_get_size(md->map)) { up_write(&md->lock); return -EINVAL; @@ -820,6 +842,42 @@ return 0; } +/*----------------------------------------------------------------- + * Event notification. + *---------------------------------------------------------------*/ +uint32_t dm_get_event_nr(struct mapped_device *md) +{ + uint32_t r; + + down_read(&md->lock); + r = md->event_nr; + up_read(&md->lock); + + return r; +} + +int dm_add_wait_queue(struct mapped_device *md, wait_queue_t *wq, + uint32_t event_nr) +{ + down_write(&md->lock); + if (event_nr != md->event_nr) { + up_write(&md->lock); + return 1; + } + + add_wait_queue(&md->eventq, wq); + up_write(&md->lock); + + return 0; +} + +void dm_remove_wait_queue(struct mapped_device *md, wait_queue_t *wq) +{ + down_write(&md->lock); + remove_wait_queue(&md->eventq, wq); + up_write(&md->lock); +} + /* * The gendisk is only valid as long as you have a reference * count on 'md'. @@ -835,7 +893,8 @@ down_read(&md->lock); t = md->map; - dm_table_get(t); + if (t) + dm_table_get(t); up_read(&md->lock); return t; diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/md/dm.h linux-2.6.0-test1-ac1/drivers/md/dm.h --- linux-2.6.0-test1/drivers/md/dm.h 2003-07-10 21:08:55.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/md/dm.h 2003-07-14 14:50:50.000000000 +0100 @@ -51,9 +51,8 @@ * Functions for manipulating a struct mapped_device. * Drop the reference with dm_put when you finish with the object. *---------------------------------------------------------------*/ -int dm_create(struct dm_table *table, struct mapped_device **md); -int dm_create_with_minor(unsigned int minor, struct dm_table *table, - struct mapped_device **md); +int dm_create(struct mapped_device **md); +int dm_create_with_minor(unsigned int minor, struct mapped_device **md); /* * Reference counting for md. @@ -79,6 +78,14 @@ struct dm_table *dm_get_table(struct mapped_device *md); /* + * Event functions. + */ +uint32_t dm_get_event_nr(struct mapped_device *md); +int dm_add_wait_queue(struct mapped_device *md, wait_queue_t *wq, + uint32_t event_nr); +void dm_remove_wait_queue(struct mapped_device *md, wait_queue_t *wq); + +/* * Info functions. */ struct gendisk *dm_disk(struct mapped_device *md); @@ -96,6 +103,8 @@ int dm_table_add_target(struct dm_table *t, const char *type, sector_t start, sector_t len, char *params); int dm_table_complete(struct dm_table *t); +void dm_table_event_callback(struct dm_table *t, + void (*fn)(void *), void *context); void dm_table_event(struct dm_table *t); sector_t dm_table_get_size(struct dm_table *t); struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index); @@ -104,7 +113,6 @@ unsigned int dm_table_get_num_targets(struct dm_table *t); struct list_head *dm_table_get_devices(struct dm_table *t); int dm_table_get_mode(struct dm_table *t); -void dm_table_add_wait_queue(struct dm_table *t, wait_queue_t *wq); void dm_table_suspend_targets(struct dm_table *t); void dm_table_resume_targets(struct dm_table *t); diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/md/dm-ioctl.c linux-2.6.0-test1-ac1/drivers/md/dm-ioctl.c --- linux-2.6.0-test1/drivers/md/dm-ioctl.c 2003-07-10 21:06:49.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/md/dm-ioctl.c 2003-07-14 14:50:50.000000000 +0100 @@ -1,1134 +1,13 @@ /* - * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. + * Copyright (C) 2003 Sistina Software (UK) Limited. * * This file is released under the GPL. */ -#include "dm.h" - -#include -#include -#include #include -#include -#include -#include -#include -#include - -#include - -#define DM_DRIVER_EMAIL "dm@uk.sistina.com" - -/*----------------------------------------------------------------- - * The ioctl interface needs to be able to look up devices by - * name or uuid. - *---------------------------------------------------------------*/ -struct hash_cell { - struct list_head name_list; - struct list_head uuid_list; - - char *name; - char *uuid; - struct mapped_device *md; -}; - -#define NUM_BUCKETS 64 -#define MASK_BUCKETS (NUM_BUCKETS - 1) -static struct list_head _name_buckets[NUM_BUCKETS]; -static struct list_head _uuid_buckets[NUM_BUCKETS]; - -void dm_hash_remove_all(void); - -/* - * Guards access to all three tables. - */ -static DECLARE_RWSEM(_hash_lock); - -static void init_buckets(struct list_head *buckets) -{ - unsigned int i; - - for (i = 0; i < NUM_BUCKETS; i++) - INIT_LIST_HEAD(buckets + i); -} - -int dm_hash_init(void) -{ - init_buckets(_name_buckets); - init_buckets(_uuid_buckets); - devfs_mk_dir(DM_DIR); - return 0; -} - -void dm_hash_exit(void) -{ - dm_hash_remove_all(); - devfs_remove(DM_DIR); -} - -/*----------------------------------------------------------------- - * Hash function: - * We're not really concerned with the str hash function being - * fast since it's only used by the ioctl interface. - *---------------------------------------------------------------*/ -static unsigned int hash_str(const char *str) -{ - const unsigned int hash_mult = 2654435387U; - unsigned int h = 0; - - while (*str) - h = (h + (unsigned int) *str++) * hash_mult; - - return h & MASK_BUCKETS; -} - -/*----------------------------------------------------------------- - * Code for looking up a device by name - *---------------------------------------------------------------*/ -static struct hash_cell *__get_name_cell(const char *str) -{ - struct list_head *tmp; - struct hash_cell *hc; - unsigned int h = hash_str(str); - - list_for_each (tmp, _name_buckets + h) { - hc = list_entry(tmp, struct hash_cell, name_list); - if (!strcmp(hc->name, str)) - return hc; - } - - return NULL; -} - -static struct hash_cell *__get_uuid_cell(const char *str) -{ - struct list_head *tmp; - struct hash_cell *hc; - unsigned int h = hash_str(str); - - list_for_each (tmp, _uuid_buckets + h) { - hc = list_entry(tmp, struct hash_cell, uuid_list); - if (!strcmp(hc->uuid, str)) - return hc; - } - - return NULL; -} - -/*----------------------------------------------------------------- - * Inserting, removing and renaming a device. - *---------------------------------------------------------------*/ -static inline char *kstrdup(const char *str) -{ - char *r = kmalloc(strlen(str) + 1, GFP_KERNEL); - if (r) - strcpy(r, str); - return r; -} - -static struct hash_cell *alloc_cell(const char *name, const char *uuid, - struct mapped_device *md) -{ - struct hash_cell *hc; - - hc = kmalloc(sizeof(*hc), GFP_KERNEL); - if (!hc) - return NULL; - - hc->name = kstrdup(name); - if (!hc->name) { - kfree(hc); - return NULL; - } - - if (!uuid) - hc->uuid = NULL; - - else { - hc->uuid = kstrdup(uuid); - if (!hc->uuid) { - kfree(hc->name); - kfree(hc); - return NULL; - } - } - - INIT_LIST_HEAD(&hc->name_list); - INIT_LIST_HEAD(&hc->uuid_list); - hc->md = md; - return hc; -} - -static void free_cell(struct hash_cell *hc) -{ - if (hc) { - kfree(hc->name); - kfree(hc->uuid); - kfree(hc); - } -} - -/* - * devfs stuff. - */ -static int register_with_devfs(struct hash_cell *hc) -{ - struct gendisk *disk = dm_disk(hc->md); - - devfs_mk_bdev(MKDEV(disk->major, disk->first_minor), - S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, - DM_DIR "/%s", hc->name); - return 0; -} - -static int unregister_with_devfs(struct hash_cell *hc) -{ - devfs_remove(DM_DIR"/%s", hc->name); - return 0; -} - -/* - * The kdev_t and uuid of a device can never change once it is - * initially inserted. - */ -int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md) -{ - struct hash_cell *cell; - - /* - * Allocate the new cells. - */ - cell = alloc_cell(name, uuid, md); - if (!cell) - return -ENOMEM; - - /* - * Insert the cell into all three hash tables. - */ - down_write(&_hash_lock); - if (__get_name_cell(name)) - goto bad; - - list_add(&cell->name_list, _name_buckets + hash_str(name)); - - if (uuid) { - if (__get_uuid_cell(uuid)) { - list_del(&cell->name_list); - goto bad; - } - list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); - } - register_with_devfs(cell); - dm_get(md); - up_write(&_hash_lock); - - return 0; - - bad: - up_write(&_hash_lock); - free_cell(cell); - return -EBUSY; -} - -void __hash_remove(struct hash_cell *hc) -{ - /* remove from the dev hash */ - list_del(&hc->uuid_list); - list_del(&hc->name_list); - unregister_with_devfs(hc); - dm_put(hc->md); - free_cell(hc); -} - -void dm_hash_remove_all(void) -{ - int i; - struct hash_cell *hc; - struct list_head *tmp, *n; - - down_write(&_hash_lock); - for (i = 0; i < NUM_BUCKETS; i++) { - list_for_each_safe (tmp, n, _name_buckets + i) { - hc = list_entry(tmp, struct hash_cell, name_list); - __hash_remove(hc); - } - } - up_write(&_hash_lock); -} - -int dm_hash_rename(const char *old, const char *new) -{ - char *new_name, *old_name; - struct hash_cell *hc; - - /* - * duplicate new. - */ - new_name = kstrdup(new); - if (!new_name) - return -ENOMEM; - - down_write(&_hash_lock); - - /* - * Is new free ? - */ - hc = __get_name_cell(new); - if (hc) { - DMWARN("asked to rename to an already existing name %s -> %s", - old, new); - up_write(&_hash_lock); - kfree(new_name); - return -EBUSY; - } - - /* - * Is there such a device as 'old' ? - */ - hc = __get_name_cell(old); - if (!hc) { - DMWARN("asked to rename a non existent device %s -> %s", - old, new); - up_write(&_hash_lock); - kfree(new_name); - return -ENXIO; - } - - /* - * rename and move the name cell. - */ - unregister_with_devfs(hc); - - list_del(&hc->name_list); - old_name = hc->name; - hc->name = new_name; - list_add(&hc->name_list, _name_buckets + hash_str(new_name)); - - /* rename the device node in devfs */ - register_with_devfs(hc); - - up_write(&_hash_lock); - kfree(old_name); - return 0; -} - - -/*----------------------------------------------------------------- - * Implementation of the ioctl commands - *---------------------------------------------------------------*/ - -/* - * All the ioctl commands get dispatched to functions with this - * prototype. - */ -typedef int (*ioctl_fn)(struct dm_ioctl *param, struct dm_ioctl *user); - -/* - * Check a string doesn't overrun the chunk of - * memory we copied from userland. - */ -static int valid_str(char *str, void *begin, void *end) -{ - while (((void *) str >= begin) && ((void *) str < end)) - if (!*str++) - return 0; - - return -EINVAL; -} - -static int next_target(struct dm_target_spec *last, uint32_t next, - void *begin, void *end, - struct dm_target_spec **spec, char **params) -{ - *spec = (struct dm_target_spec *) - ((unsigned char *) last + next); - *params = (char *) (*spec + 1); - - if (*spec < (last + 1) || ((void *) *spec > end)) - return -EINVAL; - - return valid_str(*params, begin, end); -} - -static int populate_table(struct dm_table *table, struct dm_ioctl *args) -{ - int r, first = 1; - unsigned int i = 0; - struct dm_target_spec *spec; - char *params; - void *begin, *end; - - if (!args->target_count) { - DMWARN("populate_table: no targets specified"); - return -EINVAL; - } - - begin = (void *) args; - end = begin + args->data_size; - - for (i = 0; i < args->target_count; i++) { - - if (first) - r = next_target((struct dm_target_spec *) args, - args->data_start, - begin, end, &spec, ¶ms); - else - r = next_target(spec, spec->next, begin, end, - &spec, ¶ms); - - if (r) { - DMWARN("unable to find target"); - return -EINVAL; - } - - r = dm_table_add_target(table, spec->target_type, - (sector_t) spec->sector_start, - (sector_t) spec->length, - params); - if (r) { - DMWARN("internal error adding target to table"); - return -EINVAL; - } - - first = 0; - } - - return dm_table_complete(table); -} - -/* - * Round up the ptr to the next 'align' boundary. Obviously - * 'align' must be a power of 2. - */ -static inline void *align_ptr(void *ptr, unsigned int align) -{ - align--; - return (void *) (((unsigned long) (ptr + align)) & ~align); -} - -/* - * Copies a dm_ioctl and an optional additional payload to - * userland. - */ -static int results_to_user(struct dm_ioctl *user, struct dm_ioctl *param, - void *data, uint32_t len) -{ - int r; - void *ptr = NULL; - - if (data) { - ptr = align_ptr(user + 1, sizeof(unsigned long)); - param->data_start = ptr - (void *) user; - } - - /* - * The version number has already been filled in, so we - * just copy later fields. - */ - r = copy_to_user(&user->data_size, ¶m->data_size, - sizeof(*param) - sizeof(param->version)); - if (r) - return -EFAULT; - - if (data) { - if (param->data_start + len > param->data_size) - return -ENOSPC; - - if (copy_to_user(ptr, data, len)) - r = -EFAULT; - } - - return r; -} - -/* - * Fills in a dm_ioctl structure, ready for sending back to - * userland. - */ -static int __info(struct mapped_device *md, struct dm_ioctl *param) -{ - struct dm_table *table; - struct block_device *bdev; - struct gendisk *disk = dm_disk(md); - - param->flags = DM_EXISTS_FLAG; - if (dm_suspended(md)) - param->flags |= DM_SUSPEND_FLAG; - - bdev = bdget_disk(disk, 0); - if (!bdev) - return -ENXIO; - - param->dev = bdev->bd_dev; - param->open_count = bdev->bd_openers; - bdput(bdev); - - if (disk->policy) - param->flags |= DM_READONLY_FLAG; - - table = dm_get_table(md); - param->target_count = dm_table_get_num_targets(table); - dm_table_put(table); - - return 0; -} - -/* - * Always use UUID for lookups if it's present, otherwise use name. - */ -static inline struct mapped_device *find_device(struct dm_ioctl *param) -{ - struct hash_cell *hc; - struct mapped_device *md = NULL; - - down_read(&_hash_lock); - hc = *param->uuid ? __get_uuid_cell(param->uuid) : - __get_name_cell(param->name); - if (hc) { - md = hc->md; - - /* - * Sneakily write in both the name and the uuid - * while we have the cell. - */ - strlcpy(param->name, hc->name, sizeof(param->name)); - if (hc->uuid) - strlcpy(param->uuid, hc->uuid, sizeof(param->uuid)); - else - param->uuid[0] = '\0'; - - dm_get(md); - } - up_read(&_hash_lock); - - return md; -} - -#define ALIGNMENT sizeof(int) -static void *_align(void *ptr, unsigned int a) -{ - register unsigned long align = --a; - - return (void *) (((unsigned long) ptr + align) & ~align); -} - -/* - * Copies device info back to user space, used by - * the create and info ioctls. - */ -static int info(struct dm_ioctl *param, struct dm_ioctl *user) -{ - struct mapped_device *md; - - param->flags = 0; - - md = find_device(param); - if (!md) - /* - * Device not found - returns cleared exists flag. - */ - goto out; - - __info(md, param); - dm_put(md); - - out: - return results_to_user(user, param, NULL, 0); -} - -static inline int get_mode(struct dm_ioctl *param) -{ - int mode = FMODE_READ | FMODE_WRITE; - - if (param->flags & DM_READONLY_FLAG) - mode = FMODE_READ; - - return mode; -} - -static int check_name(const char *name) -{ - if (name[0] == '/') { - DMWARN("invalid device name"); - return -EINVAL; - } - - return 0; -} - -static int create(struct dm_ioctl *param, struct dm_ioctl *user) -{ - int r; - struct dm_table *t; - struct mapped_device *md; - - r = check_name(param->name); - if (r) - return r; - - r = dm_table_create(&t, get_mode(param)); - if (r) - return r; - - r = populate_table(t, param); - if (r) { - dm_table_put(t); - return r; - } - - if (param->flags & DM_PERSISTENT_DEV_FLAG) - r = dm_create_with_minor(minor(to_kdev_t(param->dev)), t, &md); - else - r = dm_create(t, &md); - - if (r) { - dm_table_put(t); - return r; - } - dm_table_put(t); /* md will have grabbed its own reference */ - - set_disk_ro(dm_disk(md), (param->flags & DM_READONLY_FLAG) ? 1 : 0); - r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md); - dm_put(md); - - return r ? r : info(param, user); -} - -/* - * Build up the status struct for each target - */ -static int __status(struct mapped_device *md, struct dm_ioctl *param, - char *outbuf, size_t *len) -{ - unsigned int i, num_targets; - struct dm_target_spec *spec; - char *outptr; - status_type_t type; - struct dm_table *table = dm_get_table(md); - - if (param->flags & DM_STATUS_TABLE_FLAG) - type = STATUSTYPE_TABLE; - else - type = STATUSTYPE_INFO; - - outptr = outbuf; - - /* Get all the target info */ - num_targets = dm_table_get_num_targets(table); - for (i = 0; i < num_targets; i++) { - struct dm_target *ti = dm_table_get_target(table, i); - - if (outptr - outbuf + - sizeof(struct dm_target_spec) > param->data_size) { - dm_table_put(table); - return -ENOMEM; - } - - spec = (struct dm_target_spec *) outptr; - - spec->status = 0; - spec->sector_start = ti->begin; - spec->length = ti->len; - strlcpy(spec->target_type, ti->type->name, - sizeof(spec->target_type)); - - outptr += sizeof(struct dm_target_spec); - - /* Get the status/table string from the target driver */ - if (ti->type->status) - ti->type->status(ti, type, outptr, - outbuf + param->data_size - outptr); - else - outptr[0] = '\0'; - - outptr += strlen(outptr) + 1; - _align(outptr, ALIGNMENT); - spec->next = outptr - outbuf; - } - - param->target_count = num_targets; - *len = outptr - outbuf; - dm_table_put(table); - - return 0; -} - -/* - * Return the status of a device as a text string for each - * target. - */ -static int get_status(struct dm_ioctl *param, struct dm_ioctl *user) -{ - struct mapped_device *md; - size_t len = 0; - int ret; - char *outbuf = NULL; - - md = find_device(param); - if (!md) - /* - * Device not found - returns cleared exists flag. - */ - goto out; - - /* We haven't a clue how long the resultant data will be so - just allocate as much as userland has allowed us and make sure - we don't overun it */ - outbuf = kmalloc(param->data_size, GFP_KERNEL); - if (!outbuf) - goto out; - /* - * Get the status of all targets - */ - __status(md, param, outbuf, &len); - - /* - * Setup the basic dm_ioctl structure. - */ - __info(md, param); - - out: - if (md) - dm_put(md); - - ret = results_to_user(user, param, outbuf, len); - - if (outbuf) - kfree(outbuf); - - return ret; -} - -/* - * Wait for a device to report an event - */ -static int wait_device_event(struct dm_ioctl *param, struct dm_ioctl *user) -{ - struct mapped_device *md; - struct dm_table *table; - DECLARE_WAITQUEUE(wq, current); - - md = find_device(param); - if (!md) - /* - * Device not found - returns cleared exists flag. - */ - goto out; - - /* - * Setup the basic dm_ioctl structure. - */ - __info(md, param); - - /* - * Wait for a notification event - */ - set_current_state(TASK_INTERRUPTIBLE); - table = dm_get_table(md); - dm_table_add_wait_queue(table, &wq); - dm_table_put(table); - dm_put(md); - - schedule(); - - out: - return results_to_user(user, param, NULL, 0); -} - -/* - * Retrieves a list of devices used by a particular dm device. - */ -static int dep(struct dm_ioctl *param, struct dm_ioctl *user) -{ - int r; - unsigned int count; - struct mapped_device *md; - struct list_head *tmp; - size_t len = 0; - struct dm_target_deps *deps = NULL; - struct dm_table *table; - - md = find_device(param); - if (!md) - goto out; - table = dm_get_table(md); - - /* - * Setup the basic dm_ioctl structure. - */ - __info(md, param); - - /* - * Count the devices. - */ - count = 0; - list_for_each(tmp, dm_table_get_devices(table)) - count++; - - /* - * Allocate a kernel space version of the dm_target_status - * struct. - */ - if (array_too_big(sizeof(*deps), sizeof(*deps->dev), count)) { - dm_table_put(table); - dm_put(md); - return -ENOMEM; - } - - len = sizeof(*deps) + (sizeof(*deps->dev) * count); - deps = kmalloc(len, GFP_KERNEL); - if (!deps) { - dm_table_put(table); - dm_put(md); - return -ENOMEM; - } - - /* - * Fill in the devices. - */ - deps->count = count; - count = 0; - list_for_each(tmp, dm_table_get_devices(table)) { - struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); - deps->dev[count++] = dd->bdev->bd_dev; - } - dm_table_put(table); - dm_put(md); - - out: - r = results_to_user(user, param, deps, len); - - kfree(deps); - return r; -} - -static int remove(struct dm_ioctl *param, struct dm_ioctl *user) -{ - struct hash_cell *hc; - - down_write(&_hash_lock); - hc = *param->uuid ? __get_uuid_cell(param->uuid) : - __get_name_cell(param->name); - if (!hc) { - DMWARN("device doesn't appear to be in the dev hash table."); - up_write(&_hash_lock); - return -EINVAL; - } - - /* - * You may ask the interface to drop its reference to an - * in use device. This is no different to unlinking a - * file that someone still has open. The device will not - * actually be destroyed until the last opener closes it. - * The name and uuid of the device (both are interface - * properties) will be available for reuse immediately. - * - * You don't want to drop a _suspended_ device from the - * interface, since that will leave you with no way of - * resuming it. - */ - if (dm_suspended(hc->md)) { - DMWARN("refusing to remove a suspended device."); - up_write(&_hash_lock); - return -EPERM; - } - - __hash_remove(hc); - up_write(&_hash_lock); - return 0; -} - -static int remove_all(struct dm_ioctl *param, struct dm_ioctl *user) -{ - dm_hash_remove_all(); - return 0; -} - -static int suspend(struct dm_ioctl *param, struct dm_ioctl *user) -{ - int r; - struct mapped_device *md; - - md = find_device(param); - if (!md) - return -ENXIO; - - if (param->flags & DM_SUSPEND_FLAG) - r = dm_suspend(md); - else - r = dm_resume(md); - - dm_put(md); - return r; -} - -static int reload(struct dm_ioctl *param, struct dm_ioctl *user) -{ - int r; - struct mapped_device *md; - struct dm_table *t; - - r = dm_table_create(&t, get_mode(param)); - if (r) - return r; - - r = populate_table(t, param); - if (r) { - dm_table_put(t); - return r; - } - - md = find_device(param); - if (!md) { - dm_table_put(t); - return -ENXIO; - } - - r = dm_swap_table(md, t); - if (r) { - dm_put(md); - dm_table_put(t); - return r; - } - dm_table_put(t); /* md will have taken its own reference */ - - set_disk_ro(dm_disk(md), (param->flags & DM_READONLY_FLAG) ? 1 : 0); - dm_put(md); - - r = info(param, user); - return r; -} - -static int rename(struct dm_ioctl *param, struct dm_ioctl *user) -{ - int r; - char *new_name = (char *) param + param->data_start; - - if (valid_str(new_name, (void *) param, - (void *) param + param->data_size)) { - DMWARN("Invalid new logical volume name supplied."); - return -EINVAL; - } - - r = check_name(new_name); - if (r) - return r; - - return dm_hash_rename(param->name, new_name); -} - - -/*----------------------------------------------------------------- - * Implementation of open/close/ioctl on the special char - * device. - *---------------------------------------------------------------*/ -static ioctl_fn lookup_ioctl(unsigned int cmd) -{ - static struct { - int cmd; - ioctl_fn fn; - } _ioctls[] = { - {DM_VERSION_CMD, NULL}, /* version is dealt with elsewhere */ - {DM_REMOVE_ALL_CMD, remove_all}, - {DM_DEV_CREATE_CMD, create}, - {DM_DEV_REMOVE_CMD, remove}, - {DM_DEV_RELOAD_CMD, reload}, - {DM_DEV_RENAME_CMD, rename}, - {DM_DEV_SUSPEND_CMD, suspend}, - {DM_DEV_DEPS_CMD, dep}, - {DM_DEV_STATUS_CMD, info}, - {DM_TARGET_STATUS_CMD, get_status}, - {DM_TARGET_WAIT_CMD, wait_device_event}, - }; - - return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn; -} - -/* - * As well as checking the version compatibility this always - * copies the kernel interface version out. - */ -static int check_version(unsigned int cmd, struct dm_ioctl *user) -{ - uint32_t version[3]; - int r = 0; - - if (copy_from_user(version, user->version, sizeof(version))) - return -EFAULT; - - if ((DM_VERSION_MAJOR != version[0]) || - (DM_VERSION_MINOR < version[1])) { - DMWARN("ioctl interface mismatch: " - "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", - DM_VERSION_MAJOR, DM_VERSION_MINOR, - DM_VERSION_PATCHLEVEL, - version[0], version[1], version[2], cmd); - r = -EINVAL; - } - - /* - * Fill in the kernel version. - */ - version[0] = DM_VERSION_MAJOR; - version[1] = DM_VERSION_MINOR; - version[2] = DM_VERSION_PATCHLEVEL; - if (copy_to_user(user->version, version, sizeof(version))) - return -EFAULT; - - return r; -} - -static void free_params(struct dm_ioctl *param) -{ - vfree(param); -} - -static int copy_params(struct dm_ioctl *user, struct dm_ioctl **param) -{ - struct dm_ioctl tmp, *dmi; - - if (copy_from_user(&tmp, user, sizeof(tmp))) - return -EFAULT; - - if (tmp.data_size < sizeof(tmp)) - return -EINVAL; - - dmi = (struct dm_ioctl *) vmalloc(tmp.data_size); - if (!dmi) - return -ENOMEM; - - if (copy_from_user(dmi, user, tmp.data_size)) { - vfree(dmi); - return -EFAULT; - } - - *param = dmi; - return 0; -} - -static int validate_params(uint cmd, struct dm_ioctl *param) -{ - /* Ignores parameters */ - if (cmd == DM_REMOVE_ALL_CMD) - return 0; - - /* Unless creating, either name of uuid but not both */ - if (cmd != DM_DEV_CREATE_CMD) { - if ((!*param->uuid && !*param->name) || - (*param->uuid && *param->name)) { - DMWARN("one of name or uuid must be supplied"); - return -EINVAL; - } - } - - /* Ensure strings are terminated */ - param->name[DM_NAME_LEN - 1] = '\0'; - param->uuid[DM_UUID_LEN - 1] = '\0'; - - return 0; -} - -static int ctl_ioctl(struct inode *inode, struct file *file, - uint command, ulong u) -{ - int r = 0; - unsigned int cmd; - struct dm_ioctl *param; - struct dm_ioctl *user = (struct dm_ioctl *) u; - ioctl_fn fn = NULL; - - /* only root can play with this */ - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - if (_IOC_TYPE(command) != DM_IOCTL) - return -ENOTTY; - - cmd = _IOC_NR(command); - - /* - * Check the interface version passed in. This also - * writes out the kernels interface version. - */ - r = check_version(cmd, user); - if (r) - return r; - - /* - * Nothing more to do for the version command. - */ - if (cmd == DM_VERSION_CMD) - return 0; - - fn = lookup_ioctl(cmd); - if (!fn) { - DMWARN("dm_ctl_ioctl: unknown command 0x%x", command); - return -ENOTTY; - } - - /* - * Copy the parameters into kernel space. - */ - r = copy_params(user, ¶m); - if (r) - return r; - - r = validate_params(cmd, param); - if (r) { - free_params(param); - return r; - } - - r = fn(param, user); - free_params(param); - return r; -} - -static struct file_operations _ctl_fops = { - .ioctl = ctl_ioctl, - .owner = THIS_MODULE, -}; - -static struct miscdevice _dm_misc = { - .minor = MISC_DYNAMIC_MINOR, - .name = DM_NAME, - .devfs_name = "mapper/control", - .fops = &_ctl_fops -}; - -/* - * Create misc character device and link to DM_DIR/control. - */ -int __init dm_interface_init(void) -{ - int r; - - r = dm_hash_init(); - if (r) - return r; - - r = misc_register(&_dm_misc); - if (r) { - DMERR("misc_register failed for control device"); - dm_hash_exit(); - return r; - } - - DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR, - DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA, - DM_DRIVER_EMAIL); - return 0; - - if (misc_deregister(&_dm_misc) < 0) - DMERR("misc_deregister failed for control device"); - dm_hash_exit(); - return r; -} -void dm_interface_exit(void) -{ - if (misc_deregister(&_dm_misc) < 0) - DMERR("misc_deregister failed for control device"); - dm_hash_exit(); -} +#ifdef CONFIG_DM_IOCTL_V4 +#include "dm-ioctl-v4.c" +#else +#include "dm-ioctl-v1.c" +#endif diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/md/dm-ioctl-v1.c linux-2.6.0-test1-ac1/drivers/md/dm-ioctl-v1.c --- linux-2.6.0-test1/drivers/md/dm-ioctl-v1.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/md/dm-ioctl-v1.c 2003-07-14 14:50:50.000000000 +0100 @@ -0,0 +1,1160 @@ +/* + * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + */ + +#include "dm.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DM_DRIVER_EMAIL "dm@uk.sistina.com" + +/*----------------------------------------------------------------- + * The ioctl interface needs to be able to look up devices by + * name or uuid. + *---------------------------------------------------------------*/ +struct hash_cell { + struct list_head name_list; + struct list_head uuid_list; + + char *name; + char *uuid; + struct mapped_device *md; +}; + +#define NUM_BUCKETS 64 +#define MASK_BUCKETS (NUM_BUCKETS - 1) +static struct list_head _name_buckets[NUM_BUCKETS]; +static struct list_head _uuid_buckets[NUM_BUCKETS]; + +void dm_hash_remove_all(void); + +/* + * Guards access to all three tables. + */ +static DECLARE_RWSEM(_hash_lock); + +static void init_buckets(struct list_head *buckets) +{ + unsigned int i; + + for (i = 0; i < NUM_BUCKETS; i++) + INIT_LIST_HEAD(buckets + i); +} + +int dm_hash_init(void) +{ + init_buckets(_name_buckets); + init_buckets(_uuid_buckets); + devfs_mk_dir(DM_DIR); + return 0; +} + +void dm_hash_exit(void) +{ + dm_hash_remove_all(); + devfs_remove(DM_DIR); +} + +/*----------------------------------------------------------------- + * Hash function: + * We're not really concerned with the str hash function being + * fast since it's only used by the ioctl interface. + *---------------------------------------------------------------*/ +static unsigned int hash_str(const char *str) +{ + const unsigned int hash_mult = 2654435387U; + unsigned int h = 0; + + while (*str) + h = (h + (unsigned int) *str++) * hash_mult; + + return h & MASK_BUCKETS; +} + +/*----------------------------------------------------------------- + * Code for looking up a device by name + *---------------------------------------------------------------*/ +static struct hash_cell *__get_name_cell(const char *str) +{ + struct list_head *tmp; + struct hash_cell *hc; + unsigned int h = hash_str(str); + + list_for_each (tmp, _name_buckets + h) { + hc = list_entry(tmp, struct hash_cell, name_list); + if (!strcmp(hc->name, str)) + return hc; + } + + return NULL; +} + +static struct hash_cell *__get_uuid_cell(const char *str) +{ + struct list_head *tmp; + struct hash_cell *hc; + unsigned int h = hash_str(str); + + list_for_each (tmp, _uuid_buckets + h) { + hc = list_entry(tmp, struct hash_cell, uuid_list); + if (!strcmp(hc->uuid, str)) + return hc; + } + + return NULL; +} + +/*----------------------------------------------------------------- + * Inserting, removing and renaming a device. + *---------------------------------------------------------------*/ +static inline char *kstrdup(const char *str) +{ + char *r = kmalloc(strlen(str) + 1, GFP_KERNEL); + if (r) + strcpy(r, str); + return r; +} + +static struct hash_cell *alloc_cell(const char *name, const char *uuid, + struct mapped_device *md) +{ + struct hash_cell *hc; + + hc = kmalloc(sizeof(*hc), GFP_KERNEL); + if (!hc) + return NULL; + + hc->name = kstrdup(name); + if (!hc->name) { + kfree(hc); + return NULL; + } + + if (!uuid) + hc->uuid = NULL; + + else { + hc->uuid = kstrdup(uuid); + if (!hc->uuid) { + kfree(hc->name); + kfree(hc); + return NULL; + } + } + + INIT_LIST_HEAD(&hc->name_list); + INIT_LIST_HEAD(&hc->uuid_list); + hc->md = md; + return hc; +} + +static void free_cell(struct hash_cell *hc) +{ + if (hc) { + kfree(hc->name); + kfree(hc->uuid); + kfree(hc); + } +} + +/* + * devfs stuff. + */ +static int register_with_devfs(struct hash_cell *hc) +{ + struct gendisk *disk = dm_disk(hc->md); + + devfs_mk_bdev(MKDEV(disk->major, disk->first_minor), + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, + DM_DIR "/%s", hc->name); + return 0; +} + +static int unregister_with_devfs(struct hash_cell *hc) +{ + devfs_remove(DM_DIR"/%s", hc->name); + return 0; +} + +/* + * The kdev_t and uuid of a device can never change once it is + * initially inserted. + */ +int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md) +{ + struct hash_cell *cell; + + /* + * Allocate the new cells. + */ + cell = alloc_cell(name, uuid, md); + if (!cell) + return -ENOMEM; + + /* + * Insert the cell into all three hash tables. + */ + down_write(&_hash_lock); + if (__get_name_cell(name)) + goto bad; + + list_add(&cell->name_list, _name_buckets + hash_str(name)); + + if (uuid) { + if (__get_uuid_cell(uuid)) { + list_del(&cell->name_list); + goto bad; + } + list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); + } + register_with_devfs(cell); + dm_get(md); + up_write(&_hash_lock); + + return 0; + + bad: + up_write(&_hash_lock); + free_cell(cell); + return -EBUSY; +} + +void __hash_remove(struct hash_cell *hc) +{ + /* remove from the dev hash */ + list_del(&hc->uuid_list); + list_del(&hc->name_list); + unregister_with_devfs(hc); + dm_put(hc->md); + free_cell(hc); +} + +void dm_hash_remove_all(void) +{ + int i; + struct hash_cell *hc; + struct list_head *tmp, *n; + + down_write(&_hash_lock); + for (i = 0; i < NUM_BUCKETS; i++) { + list_for_each_safe (tmp, n, _name_buckets + i) { + hc = list_entry(tmp, struct hash_cell, name_list); + __hash_remove(hc); + } + } + up_write(&_hash_lock); +} + +int dm_hash_rename(const char *old, const char *new) +{ + char *new_name, *old_name; + struct hash_cell *hc; + + /* + * duplicate new. + */ + new_name = kstrdup(new); + if (!new_name) + return -ENOMEM; + + down_write(&_hash_lock); + + /* + * Is new free ? + */ + hc = __get_name_cell(new); + if (hc) { + DMWARN("asked to rename to an already existing name %s -> %s", + old, new); + up_write(&_hash_lock); + kfree(new_name); + return -EBUSY; + } + + /* + * Is there such a device as 'old' ? + */ + hc = __get_name_cell(old); + if (!hc) { + DMWARN("asked to rename a non existent device %s -> %s", + old, new); + up_write(&_hash_lock); + kfree(new_name); + return -ENXIO; + } + + /* + * rename and move the name cell. + */ + unregister_with_devfs(hc); + + list_del(&hc->name_list); + old_name = hc->name; + hc->name = new_name; + list_add(&hc->name_list, _name_buckets + hash_str(new_name)); + + /* rename the device node in devfs */ + register_with_devfs(hc); + + up_write(&_hash_lock); + kfree(old_name); + return 0; +} + + +/*----------------------------------------------------------------- + * Implementation of the ioctl commands + *---------------------------------------------------------------*/ + +/* + * All the ioctl commands get dispatched to functions with this + * prototype. + */ +typedef int (*ioctl_fn)(struct dm_ioctl *param, struct dm_ioctl *user); + +/* + * Check a string doesn't overrun the chunk of + * memory we copied from userland. + */ +static int valid_str(char *str, void *begin, void *end) +{ + while (((void *) str >= begin) && ((void *) str < end)) + if (!*str++) + return 0; + + return -EINVAL; +} + +static int next_target(struct dm_target_spec *last, uint32_t next, + void *begin, void *end, + struct dm_target_spec **spec, char **params) +{ + *spec = (struct dm_target_spec *) + ((unsigned char *) last + next); + *params = (char *) (*spec + 1); + + if (*spec < (last + 1) || ((void *) *spec > end)) + return -EINVAL; + + return valid_str(*params, begin, end); +} + +static int populate_table(struct dm_table *table, struct dm_ioctl *args) +{ + int r, first = 1; + unsigned int i = 0; + struct dm_target_spec *spec; + char *params; + void *begin, *end; + + if (!args->target_count) { + DMWARN("populate_table: no targets specified"); + return -EINVAL; + } + + begin = (void *) args; + end = begin + args->data_size; + + for (i = 0; i < args->target_count; i++) { + + if (first) + r = next_target((struct dm_target_spec *) args, + args->data_start, + begin, end, &spec, ¶ms); + else + r = next_target(spec, spec->next, begin, end, + &spec, ¶ms); + + if (r) { + DMWARN("unable to find target"); + return -EINVAL; + } + + r = dm_table_add_target(table, spec->target_type, + (sector_t) spec->sector_start, + (sector_t) spec->length, + params); + if (r) { + DMWARN("internal error adding target to table"); + return -EINVAL; + } + + first = 0; + } + + return dm_table_complete(table); +} + +/* + * Round up the ptr to the next 'align' boundary. Obviously + * 'align' must be a power of 2. + */ +static inline void *align_ptr(void *ptr, unsigned int align) +{ + align--; + return (void *) (((unsigned long) (ptr + align)) & ~align); +} + +/* + * Copies a dm_ioctl and an optional additional payload to + * userland. + */ +static int results_to_user(struct dm_ioctl *user, struct dm_ioctl *param, + void *data, uint32_t len) +{ + int r; + void *ptr = NULL; + + if (data) { + ptr = align_ptr(user + 1, sizeof(unsigned long)); + param->data_start = ptr - (void *) user; + } + + /* + * The version number has already been filled in, so we + * just copy later fields. + */ + r = copy_to_user(&user->data_size, ¶m->data_size, + sizeof(*param) - sizeof(param->version)); + if (r) + return -EFAULT; + + if (data) { + if (param->data_start + len > param->data_size) + return -ENOSPC; + + if (copy_to_user(ptr, data, len)) + r = -EFAULT; + } + + return r; +} + +/* + * Fills in a dm_ioctl structure, ready for sending back to + * userland. + */ +static int __info(struct mapped_device *md, struct dm_ioctl *param) +{ + struct dm_table *table; + struct block_device *bdev; + struct gendisk *disk = dm_disk(md); + + param->flags = DM_EXISTS_FLAG; + if (dm_suspended(md)) + param->flags |= DM_SUSPEND_FLAG; + + bdev = bdget_disk(disk, 0); + if (!bdev) + return -ENXIO; + + param->dev = bdev->bd_dev; + param->open_count = bdev->bd_openers; + bdput(bdev); + + if (disk->policy) + param->flags |= DM_READONLY_FLAG; + + table = dm_get_table(md); + param->target_count = dm_table_get_num_targets(table); + dm_table_put(table); + + return 0; +} + +/* + * Always use UUID for lookups if it's present, otherwise use name. + */ +static inline struct mapped_device *find_device(struct dm_ioctl *param) +{ + struct hash_cell *hc; + struct mapped_device *md = NULL; + + down_read(&_hash_lock); + hc = *param->uuid ? __get_uuid_cell(param->uuid) : + __get_name_cell(param->name); + if (hc) { + md = hc->md; + + /* + * Sneakily write in both the name and the uuid + * while we have the cell. + */ + strlcpy(param->name, hc->name, sizeof(param->name)); + if (hc->uuid) + strlcpy(param->uuid, hc->uuid, sizeof(param->uuid)); + else + param->uuid[0] = '\0'; + + dm_get(md); + } + up_read(&_hash_lock); + + return md; +} + +#define ALIGNMENT sizeof(int) +static void *_align(void *ptr, unsigned int a) +{ + register unsigned long align = --a; + + return (void *) (((unsigned long) ptr + align) & ~align); +} + +/* + * Copies device info back to user space, used by + * the create and info ioctls. + */ +static int info(struct dm_ioctl *param, struct dm_ioctl *user) +{ + struct mapped_device *md; + + param->flags = 0; + + md = find_device(param); + if (!md) + /* + * Device not found - returns cleared exists flag. + */ + goto out; + + __info(md, param); + dm_put(md); + + out: + return results_to_user(user, param, NULL, 0); +} + +static inline int get_mode(struct dm_ioctl *param) +{ + int mode = FMODE_READ | FMODE_WRITE; + + if (param->flags & DM_READONLY_FLAG) + mode = FMODE_READ; + + return mode; +} + +static int check_name(const char *name) +{ + if (name[0] == '/') { + DMWARN("invalid device name"); + return -EINVAL; + } + + return 0; +} + +static int create(struct dm_ioctl *param, struct dm_ioctl *user) +{ + int r; + struct dm_table *t; + struct mapped_device *md; + + r = check_name(param->name); + if (r) + return r; + + r = dm_table_create(&t, get_mode(param)); + if (r) + return r; + + r = populate_table(t, param); + if (r) { + dm_table_put(t); + return r; + } + + if (param->flags & DM_PERSISTENT_DEV_FLAG) + r = dm_create_with_minor(minor(to_kdev_t(param->dev)), &md); + else + r = dm_create(&md); + + if (r) { + dm_table_put(t); + return r; + } + + /* suspend the device */ + r = dm_suspend(md); + if (r) { + DMWARN("suspend failed"); + dm_table_put(t); + dm_put(md); + return r; + } + /* swap in the table */ + r = dm_swap_table(md, t); + if (r) { + DMWARN("table swap failed"); + dm_table_put(t); + dm_put(md); + return r; + } + + /* resume the device */ + r = dm_resume(md); + if (r) { + DMWARN("resume failed"); + dm_table_put(t); + dm_put(md); + return r; + } + + dm_table_put(t); /* md will have grabbed its own reference */ + + set_disk_ro(dm_disk(md), (param->flags & DM_READONLY_FLAG) ? 1 : 0); + r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md); + dm_put(md); + + return r ? r : info(param, user); +} + +/* + * Build up the status struct for each target + */ +static int __status(struct mapped_device *md, struct dm_ioctl *param, + char *outbuf, size_t *len) +{ + unsigned int i, num_targets; + struct dm_target_spec *spec; + char *outptr; + status_type_t type; + struct dm_table *table = dm_get_table(md); + + if (param->flags & DM_STATUS_TABLE_FLAG) + type = STATUSTYPE_TABLE; + else + type = STATUSTYPE_INFO; + + outptr = outbuf; + + /* Get all the target info */ + num_targets = dm_table_get_num_targets(table); + for (i = 0; i < num_targets; i++) { + struct dm_target *ti = dm_table_get_target(table, i); + + if (outptr - outbuf + + sizeof(struct dm_target_spec) > param->data_size) { + dm_table_put(table); + return -ENOMEM; + } + + spec = (struct dm_target_spec *) outptr; + + spec->status = 0; + spec->sector_start = ti->begin; + spec->length = ti->len; + strlcpy(spec->target_type, ti->type->name, + sizeof(spec->target_type)); + + outptr += sizeof(struct dm_target_spec); + + /* Get the status/table string from the target driver */ + if (ti->type->status) + ti->type->status(ti, type, outptr, + outbuf + param->data_size - outptr); + else + outptr[0] = '\0'; + + outptr += strlen(outptr) + 1; + _align(outptr, ALIGNMENT); + spec->next = outptr - outbuf; + } + + param->target_count = num_targets; + *len = outptr - outbuf; + dm_table_put(table); + + return 0; +} + +/* + * Return the status of a device as a text string for each + * target. + */ +static int get_status(struct dm_ioctl *param, struct dm_ioctl *user) +{ + struct mapped_device *md; + size_t len = 0; + int ret; + char *outbuf = NULL; + + md = find_device(param); + if (!md) + /* + * Device not found - returns cleared exists flag. + */ + goto out; + + /* We haven't a clue how long the resultant data will be so + just allocate as much as userland has allowed us and make sure + we don't overun it */ + outbuf = kmalloc(param->data_size, GFP_KERNEL); + if (!outbuf) + goto out; + /* + * Get the status of all targets + */ + __status(md, param, outbuf, &len); + + /* + * Setup the basic dm_ioctl structure. + */ + __info(md, param); + + out: + if (md) + dm_put(md); + + ret = results_to_user(user, param, outbuf, len); + + if (outbuf) + kfree(outbuf); + + return ret; +} + +/* + * Wait for a device to report an event + */ +static int wait_device_event(struct dm_ioctl *param, struct dm_ioctl *user) +{ + struct mapped_device *md; + DECLARE_WAITQUEUE(wq, current); + + md = find_device(param); + if (!md) + /* + * Device not found - returns cleared exists flag. + */ + goto out; + + /* + * Setup the basic dm_ioctl structure. + */ + __info(md, param); + + /* + * Wait for a notification event + */ + set_current_state(TASK_INTERRUPTIBLE); + if (!dm_add_wait_queue(md, &wq, dm_get_event_nr(md))) { + schedule(); + dm_remove_wait_queue(md, &wq); + } + set_current_state(TASK_RUNNING); + dm_put(md); + + out: + return results_to_user(user, param, NULL, 0); +} + +/* + * Retrieves a list of devices used by a particular dm device. + */ +static int dep(struct dm_ioctl *param, struct dm_ioctl *user) +{ + int r; + unsigned int count; + struct mapped_device *md; + struct list_head *tmp; + size_t len = 0; + struct dm_target_deps *deps = NULL; + struct dm_table *table; + + md = find_device(param); + if (!md) + goto out; + table = dm_get_table(md); + + /* + * Setup the basic dm_ioctl structure. + */ + __info(md, param); + + /* + * Count the devices. + */ + count = 0; + list_for_each(tmp, dm_table_get_devices(table)) + count++; + + /* + * Allocate a kernel space version of the dm_target_status + * struct. + */ + if (array_too_big(sizeof(*deps), sizeof(*deps->dev), count)) { + dm_table_put(table); + dm_put(md); + return -ENOMEM; + } + + len = sizeof(*deps) + (sizeof(*deps->dev) * count); + deps = kmalloc(len, GFP_KERNEL); + if (!deps) { + dm_table_put(table); + dm_put(md); + return -ENOMEM; + } + + /* + * Fill in the devices. + */ + deps->count = count; + count = 0; + list_for_each(tmp, dm_table_get_devices(table)) { + struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); + deps->dev[count++] = dd->bdev->bd_dev; + } + dm_table_put(table); + dm_put(md); + + out: + r = results_to_user(user, param, deps, len); + + kfree(deps); + return r; +} + +static int remove(struct dm_ioctl *param, struct dm_ioctl *user) +{ + struct hash_cell *hc; + + down_write(&_hash_lock); + hc = *param->uuid ? __get_uuid_cell(param->uuid) : + __get_name_cell(param->name); + if (!hc) { + DMWARN("device doesn't appear to be in the dev hash table."); + up_write(&_hash_lock); + return -EINVAL; + } + + /* + * You may ask the interface to drop its reference to an + * in use device. This is no different to unlinking a + * file that someone still has open. The device will not + * actually be destroyed until the last opener closes it. + * The name and uuid of the device (both are interface + * properties) will be available for reuse immediately. + * + * You don't want to drop a _suspended_ device from the + * interface, since that will leave you with no way of + * resuming it. + */ + if (dm_suspended(hc->md)) { + DMWARN("refusing to remove a suspended device."); + up_write(&_hash_lock); + return -EPERM; + } + + __hash_remove(hc); + up_write(&_hash_lock); + return 0; +} + +static int remove_all(struct dm_ioctl *param, struct dm_ioctl *user) +{ + dm_hash_remove_all(); + return 0; +} + +static int suspend(struct dm_ioctl *param, struct dm_ioctl *user) +{ + int r; + struct mapped_device *md; + + md = find_device(param); + if (!md) + return -ENXIO; + + if (param->flags & DM_SUSPEND_FLAG) + r = dm_suspend(md); + else + r = dm_resume(md); + + dm_put(md); + return r; +} + +static int reload(struct dm_ioctl *param, struct dm_ioctl *user) +{ + int r; + struct mapped_device *md; + struct dm_table *t; + + r = dm_table_create(&t, get_mode(param)); + if (r) + return r; + + r = populate_table(t, param); + if (r) { + dm_table_put(t); + return r; + } + + md = find_device(param); + if (!md) { + dm_table_put(t); + return -ENXIO; + } + + r = dm_swap_table(md, t); + if (r) { + dm_put(md); + dm_table_put(t); + return r; + } + dm_table_put(t); /* md will have taken its own reference */ + + set_disk_ro(dm_disk(md), (param->flags & DM_READONLY_FLAG) ? 1 : 0); + dm_put(md); + + r = info(param, user); + return r; +} + +static int rename(struct dm_ioctl *param, struct dm_ioctl *user) +{ + int r; + char *new_name = (char *) param + param->data_start; + + if (valid_str(new_name, (void *) param, + (void *) param + param->data_size)) { + DMWARN("Invalid new logical volume name supplied."); + return -EINVAL; + } + + r = check_name(new_name); + if (r) + return r; + + return dm_hash_rename(param->name, new_name); +} + + +/*----------------------------------------------------------------- + * Implementation of open/close/ioctl on the special char + * device. + *---------------------------------------------------------------*/ +static ioctl_fn lookup_ioctl(unsigned int cmd) +{ + static struct { + int cmd; + ioctl_fn fn; + } _ioctls[] = { + {DM_VERSION_CMD, NULL}, /* version is dealt with elsewhere */ + {DM_REMOVE_ALL_CMD, remove_all}, + {DM_DEV_CREATE_CMD, create}, + {DM_DEV_REMOVE_CMD, remove}, + {DM_DEV_RELOAD_CMD, reload}, + {DM_DEV_RENAME_CMD, rename}, + {DM_DEV_SUSPEND_CMD, suspend}, + {DM_DEV_DEPS_CMD, dep}, + {DM_DEV_STATUS_CMD, info}, + {DM_TARGET_STATUS_CMD, get_status}, + {DM_TARGET_WAIT_CMD, wait_device_event}, + }; + + return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn; +} + +/* + * As well as checking the version compatibility this always + * copies the kernel interface version out. + */ +static int check_version(unsigned int cmd, struct dm_ioctl *user) +{ + uint32_t version[3]; + int r = 0; + + if (copy_from_user(version, user->version, sizeof(version))) + return -EFAULT; + + if ((DM_VERSION_MAJOR != version[0]) || + (DM_VERSION_MINOR < version[1])) { + DMWARN("ioctl interface mismatch: " + "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", + DM_VERSION_MAJOR, DM_VERSION_MINOR, + DM_VERSION_PATCHLEVEL, + version[0], version[1], version[2], cmd); + r = -EINVAL; + } + + /* + * Fill in the kernel version. + */ + version[0] = DM_VERSION_MAJOR; + version[1] = DM_VERSION_MINOR; + version[2] = DM_VERSION_PATCHLEVEL; + if (copy_to_user(user->version, version, sizeof(version))) + return -EFAULT; + + return r; +} + +static void free_params(struct dm_ioctl *param) +{ + vfree(param); +} + +static int copy_params(struct dm_ioctl *user, struct dm_ioctl **param) +{ + struct dm_ioctl tmp, *dmi; + + if (copy_from_user(&tmp, user, sizeof(tmp))) + return -EFAULT; + + if (tmp.data_size < sizeof(tmp)) + return -EINVAL; + + dmi = (struct dm_ioctl *) vmalloc(tmp.data_size); + if (!dmi) + return -ENOMEM; + + if (copy_from_user(dmi, user, tmp.data_size)) { + vfree(dmi); + return -EFAULT; + } + + *param = dmi; + return 0; +} + +static int validate_params(uint cmd, struct dm_ioctl *param) +{ + /* Ignores parameters */ + if (cmd == DM_REMOVE_ALL_CMD) + return 0; + + /* Unless creating, either name of uuid but not both */ + if (cmd != DM_DEV_CREATE_CMD) { + if ((!*param->uuid && !*param->name) || + (*param->uuid && *param->name)) { + DMWARN("one of name or uuid must be supplied"); + return -EINVAL; + } + } + + /* Ensure strings are terminated */ + param->name[DM_NAME_LEN - 1] = '\0'; + param->uuid[DM_UUID_LEN - 1] = '\0'; + + return 0; +} + +static int ctl_ioctl(struct inode *inode, struct file *file, + uint command, ulong u) +{ + int r = 0; + unsigned int cmd; + struct dm_ioctl *param; + struct dm_ioctl *user = (struct dm_ioctl *) u; + ioctl_fn fn = NULL; + + /* only root can play with this */ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (_IOC_TYPE(command) != DM_IOCTL) + return -ENOTTY; + + cmd = _IOC_NR(command); + + /* + * Check the interface version passed in. This also + * writes out the kernels interface version. + */ + r = check_version(cmd, user); + if (r) + return r; + + /* + * Nothing more to do for the version command. + */ + if (cmd == DM_VERSION_CMD) + return 0; + + fn = lookup_ioctl(cmd); + if (!fn) { + DMWARN("dm_ctl_ioctl: unknown command 0x%x", command); + return -ENOTTY; + } + + /* + * Copy the parameters into kernel space. + */ + r = copy_params(user, ¶m); + if (r) + return r; + + r = validate_params(cmd, param); + if (r) { + free_params(param); + return r; + } + + r = fn(param, user); + free_params(param); + return r; +} + +static struct file_operations _ctl_fops = { + .ioctl = ctl_ioctl, + .owner = THIS_MODULE, +}; + +static struct miscdevice _dm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = DM_NAME, + .devfs_name = "mapper/control", + .fops = &_ctl_fops +}; + +/* + * Create misc character device and link to DM_DIR/control. + */ +int __init dm_interface_init(void) +{ + int r; + + r = dm_hash_init(); + if (r) + return r; + + r = misc_register(&_dm_misc); + if (r) { + DMERR("misc_register failed for control device"); + dm_hash_exit(); + return r; + } + + DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR, + DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA, + DM_DRIVER_EMAIL); + return 0; + + if (misc_deregister(&_dm_misc) < 0) + DMERR("misc_deregister failed for control device"); + dm_hash_exit(); + return r; +} + +void dm_interface_exit(void) +{ + if (misc_deregister(&_dm_misc) < 0) + DMERR("misc_deregister failed for control device"); + dm_hash_exit(); +} diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/md/dm-ioctl-v4.c linux-2.6.0-test1-ac1/drivers/md/dm-ioctl-v4.c --- linux-2.6.0-test1/drivers/md/dm-ioctl-v4.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/md/dm-ioctl-v4.c 2003-07-14 14:50:50.000000000 +0100 @@ -0,0 +1,1252 @@ +/* + * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + */ + +#include "dm.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DM_DRIVER_EMAIL "dm@uk.sistina.com" + +/*----------------------------------------------------------------- + * The ioctl interface needs to be able to look up devices by + * name or uuid. + *---------------------------------------------------------------*/ +struct hash_cell { + struct list_head name_list; + struct list_head uuid_list; + + char *name; + char *uuid; + struct mapped_device *md; + struct dm_table *new_map; +}; + +#define NUM_BUCKETS 64 +#define MASK_BUCKETS (NUM_BUCKETS - 1) +static struct list_head _name_buckets[NUM_BUCKETS]; +static struct list_head _uuid_buckets[NUM_BUCKETS]; + +void dm_hash_remove_all(void); + +/* + * Guards access to both hash tables. + */ +static DECLARE_RWSEM(_hash_lock); + +static void init_buckets(struct list_head *buckets) +{ + unsigned int i; + + for (i = 0; i < NUM_BUCKETS; i++) + INIT_LIST_HEAD(buckets + i); +} + +int dm_hash_init(void) +{ + init_buckets(_name_buckets); + init_buckets(_uuid_buckets); + devfs_mk_dir(DM_DIR); + return 0; +} + +void dm_hash_exit(void) +{ + dm_hash_remove_all(); + devfs_remove(DM_DIR); +} + +/*----------------------------------------------------------------- + * Hash function: + * We're not really concerned with the str hash function being + * fast since it's only used by the ioctl interface. + *---------------------------------------------------------------*/ +static unsigned int hash_str(const char *str) +{ + const unsigned int hash_mult = 2654435387U; + unsigned int h = 0; + + while (*str) + h = (h + (unsigned int) *str++) * hash_mult; + + return h & MASK_BUCKETS; +} + +/*----------------------------------------------------------------- + * Code for looking up a device by name + *---------------------------------------------------------------*/ +static struct hash_cell *__get_name_cell(const char *str) +{ + struct list_head *tmp; + struct hash_cell *hc; + unsigned int h = hash_str(str); + + list_for_each (tmp, _name_buckets + h) { + hc = list_entry(tmp, struct hash_cell, name_list); + if (!strcmp(hc->name, str)) + return hc; + } + + return NULL; +} + +static struct hash_cell *__get_uuid_cell(const char *str) +{ + struct list_head *tmp; + struct hash_cell *hc; + unsigned int h = hash_str(str); + + list_for_each (tmp, _uuid_buckets + h) { + hc = list_entry(tmp, struct hash_cell, uuid_list); + if (!strcmp(hc->uuid, str)) + return hc; + } + + return NULL; +} + +/*----------------------------------------------------------------- + * Inserting, removing and renaming a device. + *---------------------------------------------------------------*/ +static inline char *kstrdup(const char *str) +{ + char *r = kmalloc(strlen(str) + 1, GFP_KERNEL); + if (r) + strcpy(r, str); + return r; +} + +static struct hash_cell *alloc_cell(const char *name, const char *uuid, + struct mapped_device *md) +{ + struct hash_cell *hc; + + hc = kmalloc(sizeof(*hc), GFP_KERNEL); + if (!hc) + return NULL; + + hc->name = kstrdup(name); + if (!hc->name) { + kfree(hc); + return NULL; + } + + if (!uuid) + hc->uuid = NULL; + + else { + hc->uuid = kstrdup(uuid); + if (!hc->uuid) { + kfree(hc->name); + kfree(hc); + return NULL; + } + } + + INIT_LIST_HEAD(&hc->name_list); + INIT_LIST_HEAD(&hc->uuid_list); + hc->md = md; + hc->new_map = NULL; + return hc; +} + +static void free_cell(struct hash_cell *hc) +{ + if (hc) { + kfree(hc->name); + kfree(hc->uuid); + kfree(hc); + } +} + +/* + * devfs stuff. + */ +static int register_with_devfs(struct hash_cell *hc) +{ + struct gendisk *disk = dm_disk(hc->md); + + devfs_mk_bdev(MKDEV(disk->major, disk->first_minor), + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, + DM_DIR "/%s", hc->name); + return 0; +} + +static int unregister_with_devfs(struct hash_cell *hc) +{ + devfs_remove(DM_DIR"/%s", hc->name); + return 0; +} + +/* + * The kdev_t and uuid of a device can never change once it is + * initially inserted. + */ +int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md) +{ + struct hash_cell *cell; + + /* + * Allocate the new cells. + */ + cell = alloc_cell(name, uuid, md); + if (!cell) + return -ENOMEM; + + /* + * Insert the cell into both hash tables. + */ + down_write(&_hash_lock); + if (__get_name_cell(name)) + goto bad; + + list_add(&cell->name_list, _name_buckets + hash_str(name)); + + if (uuid) { + if (__get_uuid_cell(uuid)) { + list_del(&cell->name_list); + goto bad; + } + list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); + } + register_with_devfs(cell); + dm_get(md); + up_write(&_hash_lock); + + return 0; + + bad: + up_write(&_hash_lock); + free_cell(cell); + return -EBUSY; +} + +void __hash_remove(struct hash_cell *hc) +{ + /* remove from the dev hash */ + list_del(&hc->uuid_list); + list_del(&hc->name_list); + unregister_with_devfs(hc); + dm_put(hc->md); + if (hc->new_map) + dm_table_put(hc->new_map); + free_cell(hc); +} + +void dm_hash_remove_all(void) +{ + int i; + struct hash_cell *hc; + struct list_head *tmp, *n; + + down_write(&_hash_lock); + for (i = 0; i < NUM_BUCKETS; i++) { + list_for_each_safe (tmp, n, _name_buckets + i) { + hc = list_entry(tmp, struct hash_cell, name_list); + __hash_remove(hc); + } + } + up_write(&_hash_lock); +} + +int dm_hash_rename(const char *old, const char *new) +{ + char *new_name, *old_name; + struct hash_cell *hc; + + /* + * duplicate new. + */ + new_name = kstrdup(new); + if (!new_name) + return -ENOMEM; + + down_write(&_hash_lock); + + /* + * Is new free ? + */ + hc = __get_name_cell(new); + if (hc) { + DMWARN("asked to rename to an already existing name %s -> %s", + old, new); + up_write(&_hash_lock); + kfree(new_name); + return -EBUSY; + } + + /* + * Is there such a device as 'old' ? + */ + hc = __get_name_cell(old); + if (!hc) { + DMWARN("asked to rename a non existent device %s -> %s", + old, new); + up_write(&_hash_lock); + kfree(new_name); + return -ENXIO; + } + + /* + * rename and move the name cell. + */ + unregister_with_devfs(hc); + + list_del(&hc->name_list); + old_name = hc->name; + hc->name = new_name; + list_add(&hc->name_list, _name_buckets + hash_str(new_name)); + + /* rename the device node in devfs */ + register_with_devfs(hc); + + up_write(&_hash_lock); + kfree(old_name); + return 0; +} + +/*----------------------------------------------------------------- + * Implementation of the ioctl commands + *---------------------------------------------------------------*/ +/* + * All the ioctl commands get dispatched to functions with this + * prototype. + */ +typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size); + +static int remove_all(struct dm_ioctl *param, size_t param_size) +{ + dm_hash_remove_all(); + param->data_size = 0; + return 0; +} + +/* + * Round up the ptr to an 8-byte boundary. + */ +#define ALIGN_MASK 7 +static inline void *align_ptr(void *ptr) +{ + return (void *) (((size_t) (ptr + ALIGN_MASK)) & ~ALIGN_MASK); +} + +/* + * Retrieves the data payload buffer from an already allocated + * struct dm_ioctl. + */ +static void *get_result_buffer(struct dm_ioctl *param, size_t param_size, + size_t *len) +{ + param->data_start = align_ptr(param + 1) - (void *) param; + + if (param->data_start < param_size) + *len = param_size - param->data_start; + else + *len = 0; + + return ((void *) param) + param->data_start; +} + +static int list_devices(struct dm_ioctl *param, size_t param_size) +{ + unsigned int i; + struct hash_cell *hc; + size_t len, needed = 0; + struct gendisk *disk; + struct dm_name_list *nl, *old_nl = NULL; + + down_write(&_hash_lock); + + /* + * Loop through all the devices working out how much + * space we need. + */ + for (i = 0; i < NUM_BUCKETS; i++) { + list_for_each_entry (hc, _name_buckets + i, name_list) { + needed += sizeof(struct dm_name_list); + needed += strlen(hc->name); + needed += ALIGN_MASK; + } + } + + /* + * Grab our output buffer. + */ + nl = get_result_buffer(param, param_size, &len); + if (len < needed) { + param->flags |= DM_BUFFER_FULL_FLAG; + goto out; + } + param->data_size = param->data_start + needed; + + nl->dev = 0; /* Flags no data */ + + /* + * Now loop through filling out the names. + */ + for (i = 0; i < NUM_BUCKETS; i++) { + list_for_each_entry (hc, _name_buckets + i, name_list) { + if (old_nl) + old_nl->next = (uint32_t) ((void *) nl - + (void *) old_nl); + disk = dm_disk(hc->md); + nl->dev = MKDEV(disk->major, disk->first_minor); + nl->next = 0; + strcpy(nl->name, hc->name); + + old_nl = nl; + nl = align_ptr(((void *) ++nl) + strlen(hc->name) + 1); + } + } + + out: + up_write(&_hash_lock); + return 0; +} + +static int check_name(const char *name) +{ + if (strchr(name, '/')) { + DMWARN("invalid device name"); + return -EINVAL; + } + + return 0; +} + +/* + * Fills in a dm_ioctl structure, ready for sending back to + * userland. + */ +static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) +{ + struct gendisk *disk = dm_disk(md); + struct dm_table *table; + struct block_device *bdev; + + param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | + DM_ACTIVE_PRESENT_FLAG); + + if (dm_suspended(md)) + param->flags |= DM_SUSPEND_FLAG; + + bdev = bdget_disk(disk, 0); + if (!bdev) + return -ENXIO; + + param->dev = MKDEV(disk->major, disk->first_minor); + + /* + * Yes, this will be out of date by the time it gets back + * to userland, but it is still very useful ofr + * debugging. + */ + param->open_count = bdev->bd_openers; + bdput(bdev); + + if (disk->policy) + param->flags |= DM_READONLY_FLAG; + + param->event_nr = dm_get_event_nr(md); + + table = dm_get_table(md); + if (table) { + param->flags |= DM_ACTIVE_PRESENT_FLAG; + param->target_count = dm_table_get_num_targets(table); + dm_table_put(table); + } else + param->target_count = 0; + + return 0; +} + +static int dev_create(struct dm_ioctl *param, size_t param_size) +{ + int r; + struct mapped_device *md; + + r = check_name(param->name); + if (r) + return r; + + if (param->flags & DM_PERSISTENT_DEV_FLAG) + r = dm_create_with_minor(minor(to_kdev_t(param->dev)), &md); + else + r = dm_create(&md); + + if (r) + return r; + + r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md); + if (r) { + dm_put(md); + return r; + } + + param->flags &= ~DM_INACTIVE_PRESENT_FLAG; + + r = __dev_status(md, param); + dm_put(md); + + return r; +} + +/* + * Always use UUID for lookups if it's present, otherwise use name. + */ +static inline struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param) +{ + return *param->uuid ? + __get_uuid_cell(param->uuid) : __get_name_cell(param->name); +} + +static inline struct mapped_device *find_device(struct dm_ioctl *param) +{ + struct hash_cell *hc; + struct mapped_device *md = NULL; + + down_read(&_hash_lock); + hc = __find_device_hash_cell(param); + if (hc) { + md = hc->md; + + /* + * Sneakily write in both the name and the uuid + * while we have the cell. + */ + strncpy(param->name, hc->name, sizeof(param->name)); + if (hc->uuid) + strncpy(param->uuid, hc->uuid, sizeof(param->uuid)-1); + else + param->uuid[0] = '\0'; + + if (hc->new_map) + param->flags |= DM_INACTIVE_PRESENT_FLAG; + else + param->flags &= ~DM_INACTIVE_PRESENT_FLAG; + + dm_get(md); + } + up_read(&_hash_lock); + + return md; +} + +static int dev_remove(struct dm_ioctl *param, size_t param_size) +{ + struct hash_cell *hc; + + down_write(&_hash_lock); + hc = __find_device_hash_cell(param); + + if (!hc) { + DMWARN("device doesn't appear to be in the dev hash table."); + up_write(&_hash_lock); + return -ENXIO; + } + + __hash_remove(hc); + up_write(&_hash_lock); + param->data_size = 0; + return 0; +} + +/* + * Check a string doesn't overrun the chunk of + * memory we copied from userland. + */ +static int invalid_str(char *str, void *end) +{ + while ((void *) str < end) + if (!*str++) + return 0; + + return -EINVAL; +} + +static int dev_rename(struct dm_ioctl *param, size_t param_size) +{ + int r; + char *new_name = (char *) param + param->data_start; + + if (new_name < (char *) (param + 1) || + invalid_str(new_name, (void *) param + param_size)) { + DMWARN("Invalid new logical volume name supplied."); + return -EINVAL; + } + + r = check_name(new_name); + if (r) + return r; + + param->data_size = 0; + return dm_hash_rename(param->name, new_name); +} + +static int suspend(struct dm_ioctl *param) +{ + int r = 0; + struct mapped_device *md; + + md = find_device(param); + if (!md) + return -ENXIO; + + if (!dm_suspended(md)) + r = dm_suspend(md); + + if (!r) + r = __dev_status(md, param); + + dm_put(md); + return r; +} + +static int resume(struct dm_ioctl *param) +{ + int r = 0; + struct hash_cell *hc; + struct mapped_device *md; + struct dm_table *new_map; + + down_write(&_hash_lock); + + hc = __find_device_hash_cell(param); + if (!hc) { + DMWARN("device doesn't appear to be in the dev hash table."); + up_write(&_hash_lock); + return -ENXIO; + } + + md = hc->md; + dm_get(md); + + new_map = hc->new_map; + hc->new_map = NULL; + param->flags &= ~DM_INACTIVE_PRESENT_FLAG; + + up_write(&_hash_lock); + + /* Do we need to load a new map ? */ + if (new_map) { + /* Suspend if it isn't already suspended */ + if (!dm_suspended(md)) + dm_suspend(md); + + r = dm_swap_table(md, new_map); + if (r) { + dm_put(md); + dm_table_put(new_map); + return r; + } + + if (dm_table_get_mode(new_map) & FMODE_WRITE) + set_disk_ro(dm_disk(md), 0); + else + set_disk_ro(dm_disk(md), 1); + + dm_table_put(new_map); + } + + if (dm_suspended(md)) + r = dm_resume(md); + + if (!r) + r = __dev_status(md, param); + + dm_put(md); + return r; +} + +/* + * Set or unset the suspension state of a device. + * If the device already is in the requested state we just return its status. + */ +static int dev_suspend(struct dm_ioctl *param, size_t param_size) +{ + if (param->flags & DM_SUSPEND_FLAG) + return suspend(param); + + return resume(param); +} + +/* + * Copies device info back to user space, used by + * the create and info ioctls. + */ +static int dev_status(struct dm_ioctl *param, size_t param_size) +{ + int r; + struct mapped_device *md; + + md = find_device(param); + if (!md) + return -ENXIO; + + r = __dev_status(md, param); + dm_put(md); + return r; +} + +/* + * Wait for a device to report an event + */ +static int dev_wait(struct dm_ioctl *param, size_t param_size) +{ + int r; + struct mapped_device *md; + DECLARE_WAITQUEUE(wq, current); + + md = find_device(param); + if (!md) + return -ENXIO; + + /* + * Wait for a notification event + */ + set_current_state(TASK_INTERRUPTIBLE); + if (!dm_add_wait_queue(md, &wq, param->event_nr)) { + schedule(); + dm_remove_wait_queue(md, &wq); + } + set_current_state(TASK_RUNNING); + + /* + * The userland program is going to want to know what + * changed to trigger the event, so we may as well tell + * him and save an ioctl. + */ + r = __dev_status(md, param); + + dm_put(md); + return r; +} + +static inline int get_mode(struct dm_ioctl *param) +{ + int mode = FMODE_READ | FMODE_WRITE; + + if (param->flags & DM_READONLY_FLAG) + mode = FMODE_READ; + + return mode; +} + +static int next_target(struct dm_target_spec *last, uint32_t next, void *end, + struct dm_target_spec **spec, char **target_params) +{ + *spec = (struct dm_target_spec *) ((unsigned char *) last + next); + *target_params = (char *) (*spec + 1); + + if (*spec < (last + 1)) + return -EINVAL; + + return invalid_str(*target_params, end); +} + +static int populate_table(struct dm_table *table, + struct dm_ioctl *param, size_t param_size) +{ + int r; + unsigned int i = 0; + struct dm_target_spec *spec = (struct dm_target_spec *) param; + uint32_t next = param->data_start; + void *end = (void *) param + param_size; + char *target_params; + + if (!param->target_count) { + DMWARN("populate_table: no targets specified"); + return -EINVAL; + } + + for (i = 0; i < param->target_count; i++) { + + r = next_target(spec, next, end, &spec, &target_params); + if (r) { + DMWARN("unable to find target"); + return r; + } + + r = dm_table_add_target(table, spec->target_type, + (sector_t) spec->sector_start, + (sector_t) spec->length, + target_params); + if (r) { + DMWARN("error adding target to table"); + return r; + } + + next = spec->next; + } + + return dm_table_complete(table); +} + +static int table_load(struct dm_ioctl *param, size_t param_size) +{ + int r; + struct hash_cell *hc; + struct dm_table *t; + + r = dm_table_create(&t, get_mode(param)); + if (r) + return r; + + r = populate_table(t, param, param_size); + if (r) { + dm_table_put(t); + return r; + } + + down_write(&_hash_lock); + hc = __find_device_hash_cell(param); + if (!hc) { + DMWARN("device doesn't appear to be in the dev hash table."); + up_write(&_hash_lock); + return -ENXIO; + } + + hc->new_map = t; + param->flags |= DM_INACTIVE_PRESENT_FLAG; + + r = __dev_status(hc->md, param); + up_write(&_hash_lock); + return r; +} + +static int table_clear(struct dm_ioctl *param, size_t param_size) +{ + int r; + struct hash_cell *hc; + + down_write(&_hash_lock); + + hc = __find_device_hash_cell(param); + if (!hc) { + DMWARN("device doesn't appear to be in the dev hash table."); + up_write(&_hash_lock); + return -ENXIO; + } + + if (hc->new_map) { + dm_table_put(hc->new_map); + hc->new_map = NULL; + } + + param->flags &= ~DM_INACTIVE_PRESENT_FLAG; + + r = __dev_status(hc->md, param); + up_write(&_hash_lock); + return r; +} + +/* + * Retrieves a list of devices used by a particular dm device. + */ +static void retrieve_deps(struct dm_table *table, + struct dm_ioctl *param, size_t param_size) +{ + unsigned int count = 0; + struct list_head *tmp; + size_t len, needed; + struct dm_target_deps *deps; + + deps = get_result_buffer(param, param_size, &len); + + /* + * Count the devices. + */ + list_for_each(tmp, dm_table_get_devices(table)) + count++; + + /* + * Check we have enough space. + */ + needed = sizeof(*deps) + (sizeof(*deps->dev) * count); + if (len < needed) { + param->flags |= DM_BUFFER_FULL_FLAG; + return; + } + + /* + * Fill in the devices. + */ + deps->count = count; + count = 0; + list_for_each(tmp, dm_table_get_devices(table)) { + struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); + deps->dev[count++] = dd->bdev->bd_dev; + } + + param->data_size = param->data_start + needed; +} + +static int table_deps(struct dm_ioctl *param, size_t param_size) +{ + int r = 0; + struct mapped_device *md; + struct dm_table *table; + + md = find_device(param); + if (!md) + return -ENXIO; + + r = __dev_status(md, param); + if (r) + goto out; + + table = dm_get_table(md); + if (table) { + retrieve_deps(table, param, param_size); + dm_table_put(table); + } + + out: + dm_put(md); + return r; +} + +/* + * Build up the status struct for each target + */ +static void retrieve_status(struct dm_table *table, + struct dm_ioctl *param, size_t param_size) +{ + unsigned int i, num_targets; + struct dm_target_spec *spec; + char *outbuf, *outptr; + status_type_t type; + size_t remaining, len, used = 0; + + outptr = outbuf = get_result_buffer(param, param_size, &len); + + if (param->flags & DM_STATUS_TABLE_FLAG) + type = STATUSTYPE_TABLE; + else + type = STATUSTYPE_INFO; + + /* Get all the target info */ + num_targets = dm_table_get_num_targets(table); + for (i = 0; i < num_targets; i++) { + struct dm_target *ti = dm_table_get_target(table, i); + + remaining = len - (outptr - outbuf); + if (remaining < sizeof(struct dm_target_spec)) { + param->flags |= DM_BUFFER_FULL_FLAG; + break; + } + + spec = (struct dm_target_spec *) outptr; + + spec->status = 0; + spec->sector_start = ti->begin; + spec->length = ti->len; + strncpy(spec->target_type, ti->type->name, + sizeof(spec->target_type)); + + outptr += sizeof(struct dm_target_spec); + remaining = len - (outptr - outbuf); + + /* Get the status/table string from the target driver */ + if (ti->type->status) { + if (ti->type->status(ti, type, outptr, remaining)) { + param->flags |= DM_BUFFER_FULL_FLAG; + break; + } + } else + outptr[0] = '\0'; + + outptr += strlen(outptr) + 1; + used = param->data_start + (outptr - outbuf); + + align_ptr(outptr); + spec->next = outptr - outbuf; + } + + if (used) + param->data_size = used; + + param->target_count = num_targets; +} + +/* + * Return the status of a device as a text string for each + * target. + */ +static int table_status(struct dm_ioctl *param, size_t param_size) +{ + int r; + struct mapped_device *md; + struct dm_table *table; + + md = find_device(param); + if (!md) + return -ENXIO; + + r = __dev_status(md, param); + if (r) + goto out; + + table = dm_get_table(md); + if (table) { + retrieve_status(table, param, param_size); + dm_table_put(table); + } + + out: + dm_put(md); + return r; +} + +/*----------------------------------------------------------------- + * Implementation of open/close/ioctl on the special char + * device. + *---------------------------------------------------------------*/ +static ioctl_fn lookup_ioctl(unsigned int cmd) +{ + static struct { + int cmd; + ioctl_fn fn; + } _ioctls[] = { + {DM_VERSION_CMD, NULL}, /* version is dealt with elsewhere */ + {DM_REMOVE_ALL_CMD, remove_all}, + {DM_LIST_DEVICES_CMD, list_devices}, + + {DM_DEV_CREATE_CMD, dev_create}, + {DM_DEV_REMOVE_CMD, dev_remove}, + {DM_DEV_RENAME_CMD, dev_rename}, + {DM_DEV_SUSPEND_CMD, dev_suspend}, + {DM_DEV_STATUS_CMD, dev_status}, + {DM_DEV_WAIT_CMD, dev_wait}, + + {DM_TABLE_LOAD_CMD, table_load}, + {DM_TABLE_CLEAR_CMD, table_clear}, + {DM_TABLE_DEPS_CMD, table_deps}, + {DM_TABLE_STATUS_CMD, table_status} + }; + + return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn; +} + +/* + * As well as checking the version compatibility this always + * copies the kernel interface version out. + */ +static int check_version(unsigned int cmd, struct dm_ioctl *user) +{ + uint32_t version[3]; + int r = 0; + + if (copy_from_user(version, user->version, sizeof(version))) + return -EFAULT; + + if ((DM_VERSION_MAJOR != version[0]) || + (DM_VERSION_MINOR < version[1])) { + DMWARN("ioctl interface mismatch: " + "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", + DM_VERSION_MAJOR, DM_VERSION_MINOR, + DM_VERSION_PATCHLEVEL, + version[0], version[1], version[2], cmd); + r = -EINVAL; + } + + /* + * Fill in the kernel version. + */ + version[0] = DM_VERSION_MAJOR; + version[1] = DM_VERSION_MINOR; + version[2] = DM_VERSION_PATCHLEVEL; + if (copy_to_user(user->version, version, sizeof(version))) + return -EFAULT; + + return r; +} + +static void free_params(struct dm_ioctl *param) +{ + vfree(param); +} + +static int copy_params(struct dm_ioctl *user, struct dm_ioctl **param) +{ + struct dm_ioctl tmp, *dmi; + + if (copy_from_user(&tmp, user, sizeof(tmp))) + return -EFAULT; + + if (tmp.data_size < sizeof(tmp)) + return -EINVAL; + + dmi = (struct dm_ioctl *) vmalloc(tmp.data_size); + if (!dmi) + return -ENOMEM; + + if (copy_from_user(dmi, user, tmp.data_size)) { + vfree(dmi); + return -EFAULT; + } + + *param = dmi; + return 0; +} + +static int validate_params(uint cmd, struct dm_ioctl *param) +{ + /* Always clear this flag */ + param->flags &= ~DM_BUFFER_FULL_FLAG; + + /* Ignores parameters */ + if (cmd == DM_REMOVE_ALL_CMD || cmd == DM_LIST_DEVICES_CMD) + return 0; + + /* Unless creating, either name or uuid but not both */ + if (cmd != DM_DEV_CREATE_CMD) { + if ((!*param->uuid && !*param->name) || + (*param->uuid && *param->name)) { + DMWARN("one of name or uuid must be supplied, cmd(%u)", + cmd); + return -EINVAL; + } + } + + /* Ensure strings are terminated */ + param->name[DM_NAME_LEN - 1] = '\0'; + param->uuid[DM_UUID_LEN - 1] = '\0'; + + return 0; +} + +static int ctl_ioctl(struct inode *inode, struct file *file, + uint command, ulong u) +{ + int r = 0; + unsigned int cmd; + struct dm_ioctl *param; + struct dm_ioctl *user = (struct dm_ioctl *) u; + ioctl_fn fn = NULL; + size_t param_size; + + /* only root can play with this */ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (_IOC_TYPE(command) != DM_IOCTL) + return -ENOTTY; + + cmd = _IOC_NR(command); + + /* + * Check the interface version passed in. This also + * writes out the kernel's interface version. + */ + r = check_version(cmd, user); + if (r) + return r; + + /* + * Nothing more to do for the version command. + */ + if (cmd == DM_VERSION_CMD) + return 0; + + fn = lookup_ioctl(cmd); + if (!fn) { + DMWARN("dm_ctl_ioctl: unknown command 0x%x", command); + return -ENOTTY; + } + + /* + * Trying to avoid low memory issues when a device is + * suspended. + */ + current->flags |= PF_MEMALLOC; + + /* + * Copy the parameters into kernel space. + */ + r = copy_params(user, ¶m); + if (r) { + current->flags &= ~PF_MEMALLOC; + return r; + } + + /* + * FIXME: eventually we will remove the PF_MEMALLOC flag + * here. However the tools still do nasty things like + * 'load' while a device is suspended. + */ + + r = validate_params(cmd, param); + if (r) + goto out; + + param_size = param->data_size; + param->data_size = sizeof(*param); + r = fn(param, param_size); + + /* + * Copy the results back to userland. + */ + if (!r && copy_to_user(user, param, param->data_size)) + r = -EFAULT; + + out: + free_params(param); + current->flags &= ~PF_MEMALLOC; + return r; +} + +static struct file_operations _ctl_fops = { + .ioctl = ctl_ioctl, + .owner = THIS_MODULE, +}; + +static struct miscdevice _dm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = DM_NAME, + .devfs_name = "mapper/control", + .fops = &_ctl_fops +}; + +/* + * Create misc character device and link to DM_DIR/control. + */ +int __init dm_interface_init(void) +{ + int r; + + r = dm_hash_init(); + if (r) + return r; + + r = misc_register(&_dm_misc); + if (r) { + DMERR("misc_register failed for control device"); + dm_hash_exit(); + return r; + } + + DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR, + DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA, + DM_DRIVER_EMAIL); + return 0; +} + +void dm_interface_exit(void) +{ + if (misc_deregister(&_dm_misc) < 0) + DMERR("misc_deregister failed for control device"); + + dm_hash_exit(); +} diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/md/dm-table.c linux-2.6.0-test1-ac1/drivers/md/dm-table.c --- linux-2.6.0-test1/drivers/md/dm-table.c 2003-07-10 21:15:30.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/md/dm-table.c 2003-07-14 14:50:06.000000000 +0100 @@ -48,11 +48,9 @@ */ struct io_restrictions limits; - /* - * A waitqueue for processes waiting for something - * interesting to happen to this table. - */ - wait_queue_head_t eventq; + /* events get handed up using this callback */ + void (*event_fn)(void *); + void *event_context; }; /* @@ -222,7 +220,6 @@ return -ENOMEM; } - init_waitqueue_head(&t->eventq); t->mode = mode; *result = t; return 0; @@ -243,9 +240,6 @@ { unsigned int i; - /* destroying the table counts as an event */ - dm_table_event(t); - /* free the indexes (see dm_table_complete) */ if (t->depth >= 2) vfree(t->index[t->depth - 2]); @@ -694,9 +688,22 @@ return r; } +static spinlock_t _event_lock = SPIN_LOCK_UNLOCKED; +void dm_table_event_callback(struct dm_table *t, + void (*fn)(void *), void *context) +{ + spin_lock_irq(&_event_lock); + t->event_fn = fn; + t->event_context = context; + spin_unlock_irq(&_event_lock); +} + void dm_table_event(struct dm_table *t) { - wake_up_interruptible(&t->eventq); + spin_lock(&_event_lock); + if (t->event_fn) + t->event_fn(t->event_context); + spin_unlock(&_event_lock); } sector_t dm_table_get_size(struct dm_table *t) @@ -761,11 +768,6 @@ return t->mode; } -void dm_table_add_wait_queue(struct dm_table *t, wait_queue_t *wq) -{ - add_wait_queue(&t->eventq, wq); -} - void dm_table_suspend_targets(struct dm_table *t) { int i; diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/md/Kconfig linux-2.6.0-test1-ac1/drivers/md/Kconfig --- linux-2.6.0-test1/drivers/md/Kconfig 2003-07-10 21:05:23.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/md/Kconfig 2003-07-14 14:50:50.000000000 +0100 @@ -145,5 +145,12 @@ If unsure, say N. +config DM_IOCTL_V4 + bool "ioctl interface version 4" + depends on BLK_DEV_DM + ---help--- + Recent tools use a new version of the ioctl interface, only + select this option if you intend using such tools. + endmenu diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/net/ni65.c linux-2.6.0-test1-ac1/drivers/net/ni65.c --- linux-2.6.0-test1/drivers/net/ni65.c 2003-07-10 21:06:49.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/net/ni65.c 2003-07-14 18:38:10.000000000 +0100 @@ -245,6 +245,7 @@ int cmdr_addr; int cardno; int features; + spinlock_t ring_lock; }; static int ni65_probe1(struct net_device *dev,int); @@ -299,7 +300,7 @@ int irqval = request_irq(dev->irq, &ni65_interrupt,0, cards[p->cardno].cardname,dev); if (irqval) { - printk ("%s: unable to get IRQ %d (irqval=%d).\n", + printk(KERN_ERR "%s: unable to get IRQ %d (irqval=%d).\n", dev->name,dev->irq, irqval); return -EAGAIN; } @@ -409,12 +410,14 @@ p = (struct priv *) dev->priv; p->cmdr_addr = ioaddr + cards[i].cmd_offset; p->cardno = i; + spin_lock_init(&p->ring_lock); - printk("%s: %s found at %#3x, ", dev->name, cards[p->cardno].cardname , ioaddr); + printk(KERN_INFO "%s: %s found at %#3x, ", dev->name, cards[p->cardno].cardname , ioaddr); outw(inw(PORT+L_RESET),PORT+L_RESET); /* first: reset the card */ if( (j=readreg(CSR0)) != 0x4) { - printk(KERN_ERR "can't RESET card: %04x\n",j); + printk("failed.\n"); + printk(KERN_ERR "%s: Can't RESET card: %04x\n", dev->name, j); ni65_free_buffer(p); release_region(ioaddr, cards[p->cardno].total_size); return -EAGAIN; @@ -467,7 +470,8 @@ break; } if(i == 5) { - printk("Can't detect DMA channel!\n"); + printk("failed.\n"); + printk(KERN_ERR "%s: Can't detect DMA channel!\n", dev->name); ni65_free_buffer(p); release_region(ioaddr, cards[p->cardno].total_size); return -EAGAIN; @@ -480,13 +484,13 @@ if(dev->irq < 2) { - unsigned long irq_mask, delay; + unsigned long irq_mask; ni65_init_lance(p,dev->dev_addr,0,0); irq_mask = probe_irq_on(); writereg(CSR0_INIT|CSR0_INEA,CSR0); /* trigger interrupt */ - delay = jiffies + HZ/50; - while (time_before(jiffies, delay)) ; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/50); dev->irq = probe_irq_off(irq_mask); if(!dev->irq) { @@ -503,7 +507,7 @@ if(request_dma(dev->dma, cards[p->cardno].cardname ) != 0) { - printk("%s: Can't request dma-channel %d\n",dev->name,(int) dev->dma); + printk(KERN_ERR "%s: Can't request dma-channel %d\n",dev->name,(int) dev->dma); ni65_free_buffer(p); release_region(ioaddr, cards[p->cardno].total_size); return -EAGAIN; @@ -570,7 +574,7 @@ if(type) { ret = skb = alloc_skb(2+16+size,GFP_KERNEL|GFP_DMA); if(!skb) { - printk("%s: unable to allocate %s memory.\n",dev->name,what); + printk(KERN_WARNING "%s: unable to allocate %s memory.\n",dev->name,what); return NULL; } skb->dev = dev; @@ -581,12 +585,12 @@ else { ret = ptr = kmalloc(T_BUF_SIZE,GFP_KERNEL | GFP_DMA); if(!ret) { - printk("%s: unable to allocate %s memory.\n",dev->name,what); + printk(KERN_WARNING "%s: unable to allocate %s memory.\n",dev->name,what); return NULL; } } if( (u32) virt_to_phys(ptr+size) > 0x1000000) { - printk("%s: unable to allocate %s memory in lower 16MB!\n",dev->name,what); + printk(KERN_WARNING "%s: unable to allocate %s memory in lower 16MB!\n",dev->name,what); if(type) kfree_skb(skb); else @@ -692,7 +696,7 @@ writedatareg(CSR0_STOP); if(debuglevel > 1) - printk("ni65_stop_start\n"); + printk(KERN_DEBUG "ni65_stop_start\n"); if(p->features & INIT_RING_BEFORE_START) { int i; @@ -846,6 +850,8 @@ p = (struct priv *) dev->priv; + spin_lock(&p->ring_lock); + while(--bcnt) { csr0 = inw(PORT+L_DATAREG); @@ -867,7 +873,7 @@ { struct priv *p = (struct priv *) dev->priv; if(debuglevel > 1) - printk("%s: general error: %04x.\n",dev->name,csr0); + printk(KERN_ERR "%s: general error: %04x.\n",dev->name,csr0); if(csr0 & CSR0_BABL) p->stats.tx_errors++; if(csr0 & CSR0_MISS) { @@ -879,7 +885,7 @@ } if(csr0 & CSR0_MERR) { if(debuglevel > 1) - printk("%s: Ooops .. memory error: %04x.\n",dev->name,csr0); + printk(KERN_ERR "%s: Ooops .. memory error: %04x.\n",dev->name,csr0); ni65_stop_start(dev,p); } } @@ -932,12 +938,13 @@ #endif if( (csr0 & (CSR0_RXON | CSR0_TXON)) != (CSR0_RXON | CSR0_TXON) ) { - printk("%s: RX or TX was offline -> restart\n",dev->name); + printk(KERN_DEBUG "%s: RX or TX was offline -> restart\n",dev->name); ni65_stop_start(dev,p); } else writedatareg(CSR0_INEA); + spin_unlock(&p->ring_lock); return IRQ_HANDLED; } @@ -1147,9 +1154,7 @@ memset((char *)p->tmdbounce[p->tmdbouncenum]+skb->len, 0, len-skb->len); dev_kfree_skb (skb); - save_flags(flags); - cli(); - + spin_lock_irqsave(&p->ring_lock, flags); tmdp = p->tmdhead + p->tmdnum; tmdp->u.buffer = (u32) isa_virt_to_bus(p->tmdbounce[p->tmdbouncenum]); p->tmdbouncenum = (p->tmdbouncenum + 1) & (TMDNUM - 1); @@ -1157,8 +1162,7 @@ #ifdef XMT_VIA_SKB } else { - save_flags(flags); - cli(); + spin_lock_irqsave(&p->ring_lock, flags); tmdp = p->tmdhead + p->tmdnum; tmdp->u.buffer = (u32) isa_virt_to_bus(skb->data); @@ -1178,8 +1182,8 @@ p->lock = 0; dev->trans_start = jiffies; - - restore_flags(flags); + + spin_unlock_irqrestore(&p->ring_lock, flags); } return 0; @@ -1238,10 +1242,8 @@ { struct priv *p; p = (struct priv *) dev_ni65.priv; - if(!p) { - printk("Ooops .. no private struct\n"); - return; - } + if(!p) + BUG(); disable_dma(dev_ni65.dma); free_dma(dev_ni65.dma); unregister_netdev(&dev_ni65); @@ -1250,6 +1252,7 @@ dev_ni65.priv = NULL; } #endif /* MODULE */ + MODULE_LICENSE("GPL"); /* diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/net/ni65.h linux-2.6.0-test1-ac1/drivers/net/ni65.h --- linux-2.6.0-test1/drivers/net/ni65.h 2003-07-10 21:15:01.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/net/ni65.h 2003-07-14 18:31:52.000000000 +0100 @@ -20,32 +20,32 @@ #define CSR0_BABL 0x4000 /* Babble transmitter timeout error (RC) */ #define CSR0_CERR 0x2000 /* Collision Error (RC) */ #define CSR0_MISS 0x1000 /* Missed packet (RC) */ -#define CSR0_MERR 0x0800 /* Memory Error (RC) */ +#define CSR0_MERR 0x0800 /* Memory Error (RC) */ #define CSR0_RINT 0x0400 /* Receiver Interrupt (RC) */ -#define CSR0_TINT 0x0200 /* Transmit Interrupt (RC) */ +#define CSR0_TINT 0x0200 /* Transmit Interrupt (RC) */ #define CSR0_IDON 0x0100 /* Initialization Done (RC) */ #define CSR0_INTR 0x0080 /* Interrupt Flag (R) */ #define CSR0_INEA 0x0040 /* Interrupt Enable (RW) */ #define CSR0_RXON 0x0020 /* Receiver on (R) */ -#define CSR0_TXON 0x0010 /* Transmitter on (R) */ +#define CSR0_TXON 0x0010 /* Transmitter on (R) */ #define CSR0_TDMD 0x0008 /* Transmit Demand (RS) */ -#define CSR0_STOP 0x0004 /* Stop (RS) */ +#define CSR0_STOP 0x0004 /* Stop (RS) */ #define CSR0_STRT 0x0002 /* Start (RS) */ #define CSR0_INIT 0x0001 /* Initialize (RS) */ -#define CSR0_CLRALL 0x7f00 /* mask for all clearable bits */ +#define CSR0_CLRALL 0x7f00 /* mask for all clearable bits */ /* * Initialization Block Mode operation Bit Definitions. */ #define M_PROM 0x8000 /* Promiscuous Mode */ -#define M_INTL 0x0040 /* Internal Loopback */ -#define M_DRTY 0x0020 /* Disable Retry */ +#define M_INTL 0x0040 /* Internal Loopback */ +#define M_DRTY 0x0020 /* Disable Retry */ #define M_COLL 0x0010 /* Force Collision */ #define M_DTCR 0x0008 /* Disable Transmit CRC) */ #define M_LOOP 0x0004 /* Loopback */ -#define M_DTX 0x0002 /* Disable the Transmitter */ -#define M_DRX 0x0001 /* Disable the Receiver */ +#define M_DTX 0x0002 /* Disable the Transmitter */ +#define M_DRX 0x0001 /* Disable the Receiver */ /* @@ -56,7 +56,7 @@ #define RCV_ERR 0x40 /* Error Summary */ #define RCV_FRAM 0x20 /* Framing Error */ #define RCV_OFLO 0x10 /* Overflow Error */ -#define RCV_CRC 0x08 /* CRC Error */ +#define RCV_CRC 0x08 /* CRC Error */ #define RCV_BUF_ERR 0x04 /* Buffer Error */ #define RCV_START 0x02 /* Start of Packet */ #define RCV_END 0x01 /* End of Packet */ @@ -67,7 +67,7 @@ */ #define XMIT_OWN 0x80 /* owner bit 0 = host, 1 = lance */ -#define XMIT_ERR 0x40 /* Error Summary */ +#define XMIT_ERR 0x40 /* Error Summary */ #define XMIT_RETRY 0x10 /* more the 1 retry needed to Xmit */ #define XMIT_1_RETRY 0x08 /* one retry needed to Xmit */ #define XMIT_DEF 0x04 /* Deferred */ @@ -78,53 +78,44 @@ * transmit status (2) (valid if XMIT_ERR == 1) */ -#define XMIT_TDRMASK 0x03ff /* time-domain-reflectometer-value */ -#define XMIT_RTRY 0x0400 /* Failed after 16 retransmissions */ -#define XMIT_LCAR 0x0800 /* Loss of Carrier */ -#define XMIT_LCOL 0x1000 /* Late collision */ -#define XMIT_RESERV 0x2000 /* Reserved */ -#define XMIT_UFLO 0x4000 /* Underflow (late memory) */ -#define XMIT_BUFF 0x8000 /* Buffering error (no ENP) */ - -struct init_block -{ - unsigned short mode; - unsigned char eaddr[6]; - unsigned char filter[8]; - /* bit 29-31: number of rmd's (power of 2) */ - u32 rrp; /* receive ring pointer (align 8) */ - /* bit 29-31: number of tmd's (power of 2) */ - u32 trp; /* transmit ring pointer (align 8) */ +#define XMIT_TDRMASK 0x03ff /* time-domain-reflectometer-value */ +#define XMIT_RTRY 0x0400 /* Failed after 16 retransmissions */ +#define XMIT_LCAR 0x0800 /* Loss of Carrier */ +#define XMIT_LCOL 0x1000 /* Late collision */ +#define XMIT_RESERV 0x2000 /* Reserved */ +#define XMIT_UFLO 0x4000 /* Underflow (late memory) */ +#define XMIT_BUFF 0x8000 /* Buffering error (no ENP) */ + +struct init_block { + unsigned short mode; + unsigned char eaddr[6]; + unsigned char filter[8]; + /* bit 29-31: number of rmd's (power of 2) */ + u32 rrp; /* receive ring pointer (align 8) */ + /* bit 29-31: number of tmd's (power of 2) */ + u32 trp; /* transmit ring pointer (align 8) */ }; -struct rmd /* Receive Message Descriptor */ -{ - union - { - volatile u32 buffer; - struct - { - volatile unsigned char dummy[3]; - volatile unsigned char status; - } s; - } u; - volatile short blen; - volatile unsigned short mlen; +struct rmd { /* Receive Message Descriptor */ + union { + volatile u32 buffer; + struct { + volatile unsigned char dummy[3]; + volatile unsigned char status; + } s; + } u; + volatile short blen; + volatile unsigned short mlen; }; -struct tmd -{ - union - { - volatile u32 buffer; - struct - { - volatile unsigned char dummy[3]; - volatile unsigned char status; - } s; - } u; - volatile unsigned short blen; - volatile unsigned short status2; +struct tmd { + union { + volatile u32 buffer; + struct { + volatile unsigned char dummy[3]; + volatile unsigned char status; + } s; + } u; + volatile unsigned short blen; + volatile unsigned short status2; }; - - diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/net/pcmcia/3c574_cs.c linux-2.6.0-test1-ac1/drivers/net/pcmcia/3c574_cs.c --- linux-2.6.0-test1/drivers/net/pcmcia/3c574_cs.c 2003-07-14 14:11:56.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/net/pcmcia/3c574_cs.c 2003-07-14 14:16:39.000000000 +0100 @@ -1048,7 +1048,7 @@ } if (lp->fast_poll) { lp->fast_poll--; - lp->media.expires = jiffies + 2*HZ/100; + lp->media.expires = jiffies + 2; add_timer(&lp->media); return; } diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/net/pcmcia/3c589_cs.c linux-2.6.0-test1-ac1/drivers/net/pcmcia/3c589_cs.c --- linux-2.6.0-test1/drivers/net/pcmcia/3c589_cs.c 2003-07-14 14:11:56.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/net/pcmcia/3c589_cs.c 2003-07-14 14:16:39.000000000 +0100 @@ -921,7 +921,7 @@ } if (lp->fast_poll) { lp->fast_poll--; - lp->media.expires = jiffies + HZ/100; + lp->media.expires = jiffies + 1; add_timer(&lp->media); return; } diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/net/pcmcia/smc91c92_cs.c linux-2.6.0-test1-ac1/drivers/net/pcmcia/smc91c92_cs.c --- linux-2.6.0-test1/drivers/net/pcmcia/smc91c92_cs.c 2003-07-14 14:11:56.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/net/pcmcia/smc91c92_cs.c 2003-07-14 14:17:50.000000000 +0100 @@ -1996,7 +1996,7 @@ } if (smc->fast_poll) { smc->fast_poll--; - smc->media.expires = jiffies + HZ/100; + smc->media.expires = jiffies + 1; add_timer(&smc->media); SMC_SELECT_BANK(saved_bank); return; Binary files linux-2.6.0-test1/drivers/pci/gen-devlist and linux-2.6.0-test1-ac1/drivers/pci/gen-devlist differ diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/pcmcia/ricoh.h linux-2.6.0-test1-ac1/drivers/pcmcia/ricoh.h --- linux-2.6.0-test1/drivers/pcmcia/ricoh.h 2003-07-14 14:11:56.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/pcmcia/ricoh.h 2003-07-14 15:42:47.000000000 +0100 @@ -116,6 +116,8 @@ #define RL5C4XX_CMD_SHIFT 4 #define RL5C4XX_HOLD_MASK 0x1c00 #define RL5C4XX_HOLD_SHIFT 10 +#define RL5C4XX_MISC_CONTROL 0x2F /* 8 bit */ +#define RL5C4XX_ZV_ENABLE 0x08 #ifdef __YENTA_H @@ -125,10 +127,41 @@ #define rl_mem(socket) ((socket)->private[3]) #define rl_config(socket) ((socket)->private[4]) +static void ricoh_zoom_video(struct pcmcia_socket *sock, int onoff) +{ + u8 reg; + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + + reg = config_readb(socket, RL5C4XX_MISC_CONTROL); + if (onoff) + /* Zoom zoom, we will all go together, zoom zoom, zoom zoom */ + reg |= RL5C4XX_ZV_ENABLE; + else + reg &= ~RL5C4XX_ZV_ENABLE; + + config_writeb(socket, RL5C4XX_MISC_CONTROL, reg); +} + +static void ricoh_set_zv(struct pcmcia_socket *sock) +{ + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + if(socket->dev->vendor == PCI_VENDOR_ID_RICOH) + { + switch(socket->dev->device) + { + /* There may be more .. */ + case PCI_DEVICE_ID_RICOH_RL5C478: + sock->zoom_video = ricoh_zoom_video; + break; + } + } +} + static int ricoh_init(struct pcmcia_socket *sock) { struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); yenta_init(sock); + ricoh_set_zv(sock); config_writew(socket, RL5C4XX_MISC, rl_misc(socket)); config_writew(socket, RL5C4XX_16BIT_CTL, rl_ctl(socket)); diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/pcmcia/ti113x.h linux-2.6.0-test1-ac1/drivers/pcmcia/ti113x.h --- linux-2.6.0-test1/drivers/pcmcia/ti113x.h 2003-07-14 14:11:56.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/pcmcia/ti113x.h 2003-07-14 15:41:34.000000000 +0100 @@ -148,14 +148,96 @@ return 0; } +/* + * Zoom video control for TI122x/113x chips + */ + +static void ti_zoom_video(struct pcmcia_socket *sock, int onoff) +{ + u8 reg; + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + + /* If we don't have a Zoom Video switch this is harmless, + we just tristate the unused (ZV) lines */ + reg = config_readb(socket, TI113X_CARD_CONTROL); + if (onoff) + /* Zoom zoom, we will all go together, zoom zoom, zoom zoom */ + reg |= TI113X_CCR_ZVENABLE; + else + reg &= ~TI113X_CCR_ZVENABLE; + config_writeb(socket, TI113X_CARD_CONTROL, reg); +} + +/* + * The 145x series can also use this. They have an additional + * ZV autodetect mode we don't use but don't actually need. + * FIXME: manual says its in func0 and func1 but disagrees with + * itself about this - do we need to force func0, if so we need + * to know a lot more about socket pairings in pcmcia_socket than + * we do now.. uggh. + */ + +static void ti1250_zoom_video(struct pcmcia_socket *sock, int onoff) +{ + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + int shift = 0; + u8 reg; + + ti_zoom_video(sock, onoff); + + reg = config_readb(socket, 0x84); + reg |= (1<<7); /* ZV bus enable */ + + if(PCI_FUNC(socket->dev->devfn)==1) + shift = 1; + + if(onoff) + { + reg &= ~(1<<6); /* Clear select bit */ + reg |= shift<<6; /* Favour our socket */ + reg |= 1<dev->vendor == PCI_VENDOR_ID_TI) + { + switch(socket->dev->device) + { + /* There may be more .. */ + case PCI_DEVICE_ID_TI_1220: + case PCI_DEVICE_ID_TI_1221: + case PCI_DEVICE_ID_TI_1225: + sock->zoom_video = ti_zoom_video; + break; + case PCI_DEVICE_ID_TI_1250: + case PCI_DEVICE_ID_TI_1251A: + case PCI_DEVICE_ID_TI_1251B: + case PCI_DEVICE_ID_TI_1450: + sock->zoom_video = ti1250_zoom_video; + } + } +} static int ti_init(struct pcmcia_socket *sock) { struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); yenta_init(sock); + ti_set_zv(sock); ti_intctl(socket); return 0; } + /* * Generic TI init - TI has an extension for the * INTCTL register that sets the PCI CSC interrupt. @@ -176,9 +258,6 @@ if (new != reg) exca_writeb(socket, I365_INTCTL, new); -#if 0 - /* THIS CAUSES HANGS! Disabled for now, do not know why */ - /* * If ISA interrupts don't work, then fall back to routing card * interrupts to the PCI interrupt of the socket. @@ -190,7 +269,7 @@ u8 irqmux, devctl; devctl = config_readb(socket, TI113X_DEVICE_CONTROL); - if (devctl & TI113X_DCR_IMODE_MASK != TI12XX_DCR_IMODE_ALL_SERIAL) { + if ((devctl & TI113X_DCR_IMODE_MASK) != TI12XX_DCR_IMODE_ALL_SERIAL) { printk (KERN_INFO "ti113x: Routing card interrupts to PCI\n"); devctl &= ~TI113X_DCR_IMODE_MASK; @@ -203,7 +282,6 @@ config_writeb(socket, TI113X_DEVICE_CONTROL, devctl); } } -#endif socket->socket.ops->init = ti_init; return 0; @@ -220,6 +298,7 @@ { struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); yenta_init(sock); + ti_set_zv(sock); config_writel(socket, TI113X_SYSTEM_CONTROL, ti_sysctl(socket)); config_writeb(socket, TI113X_CARD_CONTROL, ti_cardctl(socket)); @@ -248,6 +327,7 @@ struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); yenta_init(sock); ti113x_init(sock); + ti_set_zv(sock); ti_irqmux(socket) = config_readl(socket, TI122X_IRQMUX); ti_irqmux(socket) = (ti_irqmux(socket) & ~0x0f) | 0x02; /* route INTA */ if (!(ti_sysctl(socket) & TI122X_SCR_INTRTIE)) diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/pcmcia/yenta_socket.c linux-2.6.0-test1-ac1/drivers/pcmcia/yenta_socket.c --- linux-2.6.0-test1/drivers/pcmcia/yenta_socket.c 2003-07-14 14:11:56.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/pcmcia/yenta_socket.c 2003-07-14 15:34:36.000000000 +0100 @@ -297,6 +297,8 @@ } exca_writeb(socket, I365_CSCINT, reg); exca_readb(socket, I365_CSC); + if(sock->zoom_video) + sock->zoom_video(sock, state->flags & SS_ZVCARD); } config_writew(socket, CB_BRIDGE_CONTROL, bridge); /* Socket event mask: get card insert/remove events.. */ diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/pnp/isapnp/core.c linux-2.6.0-test1-ac1/drivers/pnp/isapnp/core.c --- linux-2.6.0-test1/drivers/pnp/isapnp/core.c 2003-07-10 21:10:50.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/pnp/isapnp/core.c 2003-07-14 19:56:48.000000000 +0100 @@ -255,14 +255,22 @@ static int isapnp_next_rdp(void) { int rdp = isapnp_rdp; + static int old_rdp = 0; + + if(old_rdp) + { + release_region(old_rdp, 1); + old_rdp = 0; + } while (rdp <= 0x3ff) { /* * We cannot use NE2000 probe spaces for ISAPnP or we * will lock up machines. */ - if ((rdp < 0x280 || rdp > 0x380) && !check_region(rdp, 1)) + if ((rdp < 0x280 || rdp > 0x380) && request_region(rdp, 1, "ISAPnP")) { isapnp_rdp = rdp; + old_rdp = rdp; return 0; } rdp += RDP_STEP; diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/scsi/aha1542.c linux-2.6.0-test1-ac1/drivers/scsi/aha1542.c --- linux-2.6.0-test1/drivers/scsi/aha1542.c 2003-07-10 21:09:39.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/scsi/aha1542.c 2003-07-14 15:49:50.000000000 +0100 @@ -67,7 +67,7 @@ int nseg, int badseg) { - printk(KERN_CRIT "sgpnt[%d:%d] page %p/0x%x length %ld\n", + printk(KERN_CRIT "sgpnt[%d:%d] page %p/0x%x length %u\n", badseg, nseg, page_address(sgpnt[badseg].page) + sgpnt[badseg].offset, SCSI_SG_PA(&sgpnt[badseg]), diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/scsi/aha1542.h linux-2.6.0-test1-ac1/drivers/scsi/aha1542.h --- linux-2.6.0-test1/drivers/scsi/aha1542.h 2003-07-10 21:12:20.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/scsi/aha1542.h 2003-07-14 15:50:01.000000000 +0100 @@ -130,7 +130,6 @@ }; static int aha1542_detect(Scsi_Host_Template *); -static int aha1542_command(Scsi_Cmnd *); static int aha1542_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); static int aha1542_abort(Scsi_Cmnd * SCpnt); static int aha1542_bus_reset(Scsi_Cmnd * SCpnt); diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/scsi/pas16.c linux-2.6.0-test1-ac1/drivers/scsi/pas16.c --- linux-2.6.0-test1/drivers/scsi/pas16.c 2003-07-10 21:04:04.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/scsi/pas16.c 2003-07-14 15:51:03.000000000 +0100 @@ -117,6 +117,7 @@ #include #include #include +#include #include #include #include diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/scsi/qla1280.c linux-2.6.0-test1-ac1/drivers/scsi/qla1280.c --- linux-2.6.0-test1/drivers/scsi/qla1280.c 2003-07-10 21:15:36.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/scsi/qla1280.c 2003-07-14 14:55:46.000000000 +0100 @@ -327,7 +327,7 @@ /* 3.16 */ #ifdef QLA_64BIT_PTR #define pci_dma_lo32(a) (a & 0xffffffff) -#define pci_dma_hi32(a) (a >> 32) +#define pci_dma_hi32(a) ((a >> 16)>>16) #else #define pci_dma_lo32(a) (a & 0xffffffff) #define pci_dma_hi32(a) 0 diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/scsi/qlogicfas.c linux-2.6.0-test1-ac1/drivers/scsi/qlogicfas.c --- linux-2.6.0-test1/drivers/scsi/qlogicfas.c 2003-07-10 21:15:40.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/scsi/qlogicfas.c 2003-07-14 15:51:53.000000000 +0100 @@ -140,6 +140,7 @@ #include #include +#include #include "scsi.h" #include "hosts.h" diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/scsi/sym53c8xx.c linux-2.6.0-test1-ac1/drivers/scsi/sym53c8xx.c --- linux-2.6.0-test1/drivers/scsi/sym53c8xx.c 2003-07-10 21:12:16.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/scsi/sym53c8xx.c 2003-07-14 15:47:07.000000000 +0100 @@ -1327,7 +1327,7 @@ #define SetScsiAbortResult(cmd) SetScsiResult(cmd, DID_ABORT, 0xff) #endif -static void sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs); +static irqreturn_t sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs); static void sym53c8xx_timeout(unsigned long np); #define initverbose (driver_setup.verbose) @@ -13660,7 +13660,7 @@ ** routine for each host that uses this IRQ. */ -static void sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs) { unsigned long flags; ncb_p np = (ncb_p) dev_id; @@ -13685,6 +13685,7 @@ ncr_flush_done_cmds(done_list); NCR_UNLOCK_SCSI_DONE(done_list->device->host, flags); } + return IRQ_HANDLED; } /* diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/serial/21285.c linux-2.6.0-test1-ac1/drivers/serial/21285.c --- linux-2.6.0-test1/drivers/serial/21285.c 2003-07-10 21:12:51.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/serial/21285.c 2003-07-14 14:57:18.000000000 +0100 @@ -481,18 +481,6 @@ } extern struct uart_driver serial21285_reg; -#ifdef CONFIG_SERIAL_21285_OLD -static struct console serial21285_old_cons = -{ - .name = SERIAL_21285_OLD_NAME, - .write = serial21285_console_write, - .device = uart_console_device, - .setup = serial21285_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &serial21285_reg, -}; -#endif static struct console serial21285_console = { diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/serial/core.c linux-2.6.0-test1-ac1/drivers/serial/core.c --- linux-2.6.0-test1/drivers/serial/core.c 2003-07-10 21:06:05.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/serial/core.c 2003-07-14 16:25:33.000000000 +0100 @@ -1667,23 +1667,25 @@ return ret + 1; } - status = port->ops->get_mctrl(port); - - ret += sprintf(buf + ret, " tx:%d rx:%d", - port->icount.tx, port->icount.rx); - if (port->icount.frame) - ret += sprintf(buf + ret, " fe:%d", - port->icount.frame); - if (port->icount.parity) - ret += sprintf(buf + ret, " pe:%d", - port->icount.parity); - if (port->icount.brk) - ret += sprintf(buf + ret, " brk:%d", - port->icount.brk); - if (port->icount.overrun) - ret += sprintf(buf + ret, " oe:%d", - port->icount.overrun); - + if(capable(CAP_SYS_ADMIN)) + { + status = port->ops->get_mctrl(port); + + ret += sprintf(buf + ret, " tx:%d rx:%d", + port->icount.tx, port->icount.rx); + if (port->icount.frame) + ret += sprintf(buf + ret, " fe:%d", + port->icount.frame); + if (port->icount.parity) + ret += sprintf(buf + ret, " pe:%d", + port->icount.parity); + if (port->icount.brk) + ret += sprintf(buf + ret, " brk:%d", + port->icount.brk); + if (port->icount.overrun) + ret += sprintf(buf + ret, " oe:%d", + port->icount.overrun); + #define INFOBIT(bit,str) \ if (port->mctrl & (bit)) \ strncat(stat_buf, (str), sizeof(stat_buf) - \ @@ -1693,19 +1695,22 @@ strncat(stat_buf, (str), sizeof(stat_buf) - \ strlen(stat_buf) - 2) - stat_buf[0] = '\0'; - stat_buf[1] = '\0'; - INFOBIT(TIOCM_RTS, "|RTS"); - STATBIT(TIOCM_CTS, "|CTS"); - INFOBIT(TIOCM_DTR, "|DTR"); - STATBIT(TIOCM_DSR, "|DSR"); - STATBIT(TIOCM_CAR, "|CD"); - STATBIT(TIOCM_RNG, "|RI"); - if (stat_buf[0]) - stat_buf[0] = ' '; - strcat(stat_buf, "\n"); - - ret += sprintf(buf + ret, stat_buf); + stat_buf[0] = '\0'; + stat_buf[1] = '\0'; + INFOBIT(TIOCM_RTS, "|RTS"); + STATBIT(TIOCM_CTS, "|CTS"); + INFOBIT(TIOCM_DTR, "|DTR"); + STATBIT(TIOCM_DSR, "|DSR"); + STATBIT(TIOCM_CAR, "|CD"); + STATBIT(TIOCM_RNG, "|RI"); + if (stat_buf[0]) + stat_buf[0] = ' '; + strcat(stat_buf, "\n"); + + ret += sprintf(buf + ret, stat_buf); + } +#undef STATBIT +#undef INFOBIT return ret; } diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/serial/Kconfig linux-2.6.0-test1-ac1/drivers/serial/Kconfig --- linux-2.6.0-test1/drivers/serial/Kconfig 2003-07-10 21:14:20.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/serial/Kconfig 2003-07-14 14:57:18.000000000 +0100 @@ -249,13 +249,6 @@ PCI bridge you can enable its onboard serial port by enabling this option. -config SERIAL_21285_OLD - bool "Use /dev/ttyS0 device (OBSOLETE)" - depends on SERIAL_21285=y && OBSOLETE - help - Use the old /dev/ttyS name, major 4 minor 64. This is obsolete - and will be removed during later 2.5 development. - config SERIAL_21285_CONSOLE bool "Console on DC21285 serial port" depends on SERIAL_21285=y diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/telephony/Kconfig linux-2.6.0-test1-ac1/drivers/telephony/Kconfig --- linux-2.6.0-test1/drivers/telephony/Kconfig 2003-07-10 21:06:01.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/telephony/Kconfig 2003-07-14 14:56:57.000000000 +0100 @@ -39,7 +39,7 @@ config PHONE_IXJ_PCMCIA tristate "QuickNet Internet LineJack/PhoneJack PCMCIA support" - depends on PHONE_IXJ + depends on PHONE_IXJ && PCMCIA help Say Y here to configure in PCMCIA service support for the Quicknet cards manufactured by Quicknet Technologies, Inc. This changes the diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/telephony/phonedev.c linux-2.6.0-test1-ac1/drivers/telephony/phonedev.c --- linux-2.6.0-test1/drivers/telephony/phonedev.c 2003-07-10 21:11:35.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/telephony/phonedev.c 2003-07-14 17:05:52.000000000 +0100 @@ -106,7 +106,6 @@ if (phone_device[i] == NULL) { phone_device[i] = p; p->minor = i; - MOD_INC_USE_COUNT; up(&phone_lock); return 0; } @@ -126,7 +125,6 @@ panic("phone: bad unregister"); phone_device[pfd->minor] = NULL; up(&phone_lock); - MOD_DEC_USE_COUNT; } diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/video/cfbimgblt.c linux-2.6.0-test1-ac1/drivers/video/cfbimgblt.c --- linux-2.6.0-test1/drivers/video/cfbimgblt.c 2003-07-10 21:10:25.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/video/cfbimgblt.c 2003-07-14 14:52:25.000000000 +0100 @@ -325,7 +325,7 @@ else slow_imageblit(image, p, dst1, fgcolor, bgcolor, start_index, pitch_index); - } else if (image->depth == bpp) + } else if (image->depth <= bpp) color_imageblit(image, p, dst1, start_index, pitch_index); } diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/drivers/video/logo/logo_linux_clut224.c linux-2.6.0-test1-ac1/drivers/video/logo/logo_linux_clut224.c --- linux-2.6.0-test1/drivers/video/logo/logo_linux_clut224.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/drivers/video/logo/logo_linux_clut224.c 2003-07-11 17:18:06.000000000 +0100 @@ -0,0 +1,606 @@ +/* + * DO NOT EDIT THIS FILE! + * + * It was automatically generated from drivers/video/logo/logo_linux_clut224.ppm + * + * Linux logo logo_linux_clut224 + */ + +#include + +static const unsigned char logo_linux_clut224_data[] __initdata = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x21, 0x21, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x26, 0x26, 0x25, 0x28, 0x23, 0x22, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x21, 0x23, 0x25, 0x2a, 0x2b, 0x2c, 0x2d, 0x2d, 0x2d, 0x2e, 0x2c, 0x2b, + 0x2a, 0x25, 0x28, 0x22, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x24, 0x2a, 0x2c, 0x2f, + 0x2c, 0x30, 0x30, 0x24, 0x25, 0x27, 0x2b, 0x2c, 0x2f, 0x31, 0x32, 0x25, + 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x29, 0x29, 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x25, 0x33, 0x34, 0x35, 0x21, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x21, 0x2b, 0x2f, 0x2c, 0x30, 0x28, 0x21, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x24, 0x33, + 0x2d, 0x27, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x21, 0x31, 0x2d, 0x32, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, 0x2a, 0x34, 0x25, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x23, 0x32, 0x27, 0x21, 0x36, + 0x2a, 0x2d, 0x2a, 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x29, 0x20, 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x26, 0x2c, 0x35, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x25, 0x2f, 0x37, 0x32, 0x22, 0x36, 0x35, 0x31, 0x27, + 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, 0x2a, 0x2f, 0x22, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x26, + 0x38, 0x38, 0x35, 0x25, 0x36, 0x21, 0x2d, 0x2b, 0x24, 0x21, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x21, 0x24, 0x39, 0x39, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x25, 0x2b, 0x30, 0x28, 0x22, + 0x36, 0x36, 0x27, 0x34, 0x30, 0x23, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x29, 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, + 0x26, 0x2d, 0x26, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x22, 0x22, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x2d, + 0x33, 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x30, 0x2f, 0x23, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x2b, 0x2c, 0x25, 0x21, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x23, 0x2a, 0x34, 0x36, 0x36, 0x36, 0x21, 0x22, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x21, 0x23, 0x22, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x28, 0x34, 0x27, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, + 0x32, 0x2f, 0x36, 0x36, 0x21, 0x21, 0x24, 0x27, 0x21, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x28, 0x27, 0x22, 0x33, 0x24, 0x36, 0x36, 0x36, 0x36, 0x22, + 0x2f, 0x2a, 0x23, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, 0x32, 0x2f, 0x36, 0x36, + 0x30, 0x3a, 0x38, 0x24, 0x24, 0x36, 0x36, 0x36, 0x23, 0x2f, 0x3b, 0x3c, + 0x3d, 0x30, 0x25, 0x21, 0x36, 0x36, 0x36, 0x36, 0x2f, 0x32, 0x23, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x23, 0x32, 0x2f, 0x36, 0x23, 0x3e, 0x3f, 0x40, 0x3a, + 0x22, 0x36, 0x36, 0x21, 0x41, 0x42, 0x43, 0x44, 0x45, 0x3e, 0x23, 0x21, + 0x36, 0x36, 0x36, 0x36, 0x2f, 0x33, 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, + 0x29, 0x29, 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, + 0x32, 0x2f, 0x36, 0x2b, 0x44, 0x40, 0x46, 0x47, 0x35, 0x36, 0x36, 0x26, + 0x43, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x2e, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x31, 0x35, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, 0x32, 0x34, 0x36, 0x4d, + 0x4e, 0x25, 0x2f, 0x46, 0x4a, 0x22, 0x23, 0x32, 0x4f, 0x50, 0x21, 0x31, + 0x51, 0x52, 0x53, 0x36, 0x36, 0x36, 0x36, 0x36, 0x31, 0x35, 0x24, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x29, 0x20, 0x29, 0x29, 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x23, 0x2a, 0x2f, 0x21, 0x3a, 0x4d, 0x21, 0x31, 0x54, + 0x55, 0x28, 0x30, 0x2b, 0x4b, 0x4d, 0x36, 0x23, 0x32, 0x50, 0x3f, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x2e, 0x39, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, + 0x29, 0x20, 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, + 0x2a, 0x38, 0x23, 0x37, 0x55, 0x36, 0x28, 0x3a, 0x56, 0x57, 0x57, 0x58, + 0x3c, 0x4d, 0x36, 0x36, 0x36, 0x40, 0x40, 0x21, 0x36, 0x36, 0x36, 0x36, + 0x2e, 0x39, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x29, 0x20, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x30, 0x51, 0x23, 0x35, + 0x43, 0x25, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x36, + 0x31, 0x47, 0x3b, 0x36, 0x36, 0x36, 0x36, 0x36, 0x31, 0x2c, 0x25, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x30, 0x2f, 0x23, 0x22, 0x40, 0x62, 0x63, 0x5d, + 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x66, 0x5e, 0x6a, 0x6b, 0x2a, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x33, 0x2e, 0x26, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x27, 0x2f, 0x23, 0x36, 0x6c, 0x63, 0x6d, 0x64, 0x5c, 0x66, 0x69, 0x6e, + 0x6f, 0x70, 0x71, 0x69, 0x69, 0x72, 0x6c, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x33, 0x34, 0x27, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x27, 0x34, 0x26, 0x73, + 0x74, 0x75, 0x76, 0x64, 0x65, 0x77, 0x69, 0x78, 0x70, 0x71, 0x71, 0x71, + 0x72, 0x5f, 0x5e, 0x21, 0x36, 0x36, 0x36, 0x36, 0x25, 0x38, 0x2a, 0x23, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x26, 0x2d, 0x33, 0x79, 0x63, 0x7a, 0x7b, 0x5c, + 0x66, 0x69, 0x6e, 0x7c, 0x71, 0x71, 0x69, 0x7d, 0x7e, 0x7a, 0x7f, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x21, 0x51, 0x2b, 0x28, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x26, 0x2d, 0x32, 0x24, 0x80, 0x81, 0x64, 0x82, 0x77, 0x69, 0x71, 0x71, + 0x69, 0x83, 0x84, 0x85, 0x7a, 0x85, 0x86, 0x36, 0x21, 0x2b, 0x23, 0x36, + 0x36, 0x39, 0x2e, 0x26, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x27, 0x2d, 0x33, 0x21, + 0x87, 0x88, 0x89, 0x72, 0x67, 0x66, 0x5f, 0x89, 0x8a, 0x63, 0x85, 0x8b, + 0x8c, 0x8d, 0x41, 0x36, 0x36, 0x2d, 0x3a, 0x35, 0x36, 0x24, 0x51, 0x32, + 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x30, 0x2f, 0x33, 0x21, 0x55, 0x8e, 0x8f, 0x8a, + 0x7d, 0x5e, 0x90, 0x7e, 0x75, 0x75, 0x90, 0x62, 0x40, 0x3f, 0x49, 0x23, + 0x36, 0x24, 0x3a, 0x3a, 0x24, 0x36, 0x2e, 0x31, 0x26, 0x22, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, + 0x33, 0x37, 0x25, 0x22, 0x3b, 0x50, 0x8e, 0x8f, 0x90, 0x7e, 0x90, 0x63, + 0x74, 0x91, 0x92, 0x42, 0x93, 0x4b, 0x45, 0x2c, 0x36, 0x36, 0x33, 0x39, + 0x21, 0x36, 0x22, 0x51, 0x33, 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x27, 0x2e, 0x2e, 0x36, 0x21, + 0x94, 0x3f, 0x50, 0x95, 0x96, 0x8f, 0x8f, 0x97, 0x8e, 0x42, 0x50, 0x43, + 0x47, 0x48, 0x48, 0x98, 0x21, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x39, + 0x2e, 0x27, 0x23, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x24, 0x2b, 0x38, 0x28, 0x36, 0x32, 0x4c, 0x4b, 0x50, 0x50, + 0x50, 0x42, 0x42, 0x50, 0x50, 0x40, 0x45, 0x99, 0x48, 0x48, 0x48, 0x48, + 0x34, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x23, 0x2f, 0x2b, 0x24, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, 0x32, 0x51, + 0x32, 0x28, 0x21, 0x98, 0x48, 0x47, 0x9a, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x9a, 0x4f, 0x9b, 0x48, 0x48, 0x48, 0x48, 0x48, 0x93, 0x23, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x2a, 0x2f, 0x2a, 0x28, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x21, 0x23, 0x30, 0x2e, 0x2c, 0x36, 0x21, 0x51, 0x9b, + 0x48, 0x48, 0x52, 0x3f, 0x50, 0x50, 0x40, 0x4b, 0x47, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x34, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x2d, 0x31, 0x27, 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, + 0x27, 0x2c, 0x2d, 0x21, 0x36, 0x28, 0x44, 0x48, 0x48, 0x48, 0x48, 0x47, + 0x46, 0x4f, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x9c, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x28, 0x51, 0x39, + 0x26, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x25, 0x35, 0x51, 0x28, 0x36, + 0x36, 0x9d, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x9b, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4f, 0x28, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x28, 0x38, 0x2b, 0x25, 0x22, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x21, 0x24, 0x33, 0x51, 0x25, 0x36, 0x36, 0x23, 0x40, 0x9b, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x9b, 0x99, 0x2b, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x30, 0x2f, 0x33, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x23, 0x30, 0x34, + 0x27, 0x36, 0x36, 0x36, 0x2a, 0x40, 0x47, 0x48, 0x48, 0x48, 0x48, 0x9b, + 0x99, 0x99, 0x9b, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x9b, 0x47, 0x52, + 0x46, 0x4f, 0x37, 0x21, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x30, 0x34, 0x2a, 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x22, 0x25, 0x39, 0x2c, 0x36, 0x36, 0x36, 0x21, + 0x31, 0x4e, 0x9a, 0x4c, 0x47, 0x9b, 0x9b, 0x52, 0x46, 0x4f, 0x52, 0x9b, + 0x9b, 0x9b, 0x47, 0x4f, 0x45, 0x9a, 0x93, 0x93, 0x3f, 0x93, 0x98, 0x28, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x39, 0x2c, 0x26, + 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x23, 0x2a, 0x34, 0x28, 0x36, 0x36, 0x36, 0x22, 0x38, 0x98, 0x44, 0x99, + 0x9b, 0x48, 0x48, 0x9b, 0x4c, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x47, + 0x52, 0x46, 0x43, 0x93, 0x40, 0x40, 0x43, 0x53, 0x21, 0x23, 0x33, 0x23, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x21, 0x2f, 0x32, 0x28, 0x21, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x24, 0x2b, 0x31, 0x36, + 0x36, 0x22, 0x36, 0x24, 0x9e, 0x4f, 0x9b, 0x48, 0x48, 0x48, 0x48, 0x9b, + 0x99, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x47, + 0x4f, 0x9a, 0x3f, 0x46, 0x38, 0x36, 0x21, 0x30, 0x26, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x39, 0x2c, 0x25, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x22, 0x26, 0x2e, 0x33, 0x36, 0x25, 0x25, 0x36, 0x4d, + 0x52, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x47, 0x44, 0x93, + 0x43, 0x23, 0x36, 0x36, 0x26, 0x24, 0x36, 0x36, 0x36, 0x36, 0x28, 0x2f, + 0x2a, 0x23, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, + 0x2a, 0x51, 0x24, 0x36, 0x2a, 0x36, 0x28, 0x44, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x9b, 0x4b, 0x44, 0x37, 0x36, 0x23, + 0x28, 0x30, 0x22, 0x36, 0x36, 0x36, 0x36, 0x2d, 0x35, 0x24, 0x21, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, 0x2b, 0x34, 0x36, 0x25, + 0x24, 0x36, 0x4a, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x9b, 0x52, 0x3f, 0x21, 0x30, 0x35, 0x25, 0x30, 0x36, + 0x36, 0x36, 0x36, 0x32, 0x2d, 0x26, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x26, 0x2e, 0x35, 0x36, 0x2a, 0x36, 0x24, 0x4f, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x9b, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x47, 0x32, 0x30, 0x2a, 0x23, 0x30, 0x23, 0x36, 0x36, 0x36, 0x21, + 0x2f, 0x32, 0x23, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x23, 0x2a, + 0x51, 0x28, 0x28, 0x25, 0x36, 0x3a, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x9b, 0x52, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x38, 0x21, + 0x36, 0x36, 0x22, 0x27, 0x36, 0x36, 0x36, 0x36, 0x2e, 0x35, 0x24, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x25, 0x2c, 0x34, 0x36, 0x30, 0x21, + 0x23, 0x43, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x47, 0x99, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x94, 0x36, 0x36, 0x36, 0x36, 0x32, + 0x36, 0x36, 0x36, 0x36, 0x2a, 0x2e, 0x26, 0x22, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x21, 0x23, 0x2a, 0x51, 0x25, 0x21, 0x2a, 0x36, 0x2e, 0x9b, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x99, 0x99, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x98, 0x36, 0x36, 0x36, 0x36, 0x32, 0x36, 0x36, 0x36, 0x36, + 0x22, 0x2f, 0x30, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x25, 0x2c, 0x34, + 0x36, 0x24, 0x28, 0x36, 0x54, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x4c, 0x99, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x9a, 0x36, + 0x36, 0x36, 0x36, 0x30, 0x36, 0x36, 0x36, 0x36, 0x21, 0x2f, 0x32, 0x23, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x21, 0x28, 0x32, 0x2f, 0x28, 0x36, 0x27, 0x22, 0x21, + 0x43, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4c, 0x99, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4b, 0x21, 0x36, 0x36, 0x21, 0x26, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x34, 0x2b, 0x28, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x25, 0x2c, 0x39, 0x36, 0x36, 0x30, 0x22, 0x25, 0x52, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4f, 0x52, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x4f, 0x21, 0x36, 0x36, 0x22, 0x26, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x2c, 0x35, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x30, 0x2d, 0x21, 0x36, + 0x36, 0x32, 0x23, 0x2a, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x4f, 0x99, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4c, 0x22, + 0x36, 0x36, 0x24, 0x23, 0x36, 0x36, 0x36, 0x36, 0x36, 0x2c, 0x39, 0x24, + 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x21, 0x28, 0x33, 0x2e, 0x36, 0x36, 0x23, 0x31, 0x27, 0x39, + 0x9b, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4f, 0x47, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4c, 0x23, 0x36, 0x36, 0x26, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x2c, 0x39, 0x24, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, + 0x2b, 0x39, 0x36, 0x36, 0x36, 0x26, 0x32, 0x31, 0x9b, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4f, 0x47, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x46, 0x22, 0x36, 0x21, 0x26, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x2c, 0x35, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x24, 0x35, 0x39, 0x36, 0x36, + 0x36, 0x36, 0x26, 0x2d, 0x9b, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x4f, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x9a, 0x36, + 0x24, 0x27, 0x9f, 0x24, 0x25, 0x28, 0x21, 0x36, 0x36, 0x34, 0x2b, 0x28, + 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x21, 0x25, 0x39, 0x4d, 0xa0, 0x84, 0x81, 0x57, 0x21, 0x39, + 0x52, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4f, 0x47, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x53, 0x28, 0x23, 0x36, 0x36, 0x36, + 0x21, 0x28, 0x2c, 0x30, 0x21, 0x38, 0x33, 0x28, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x22, 0x22, 0x28, 0x30, + 0x2d, 0xa1, 0x7a, 0xa2, 0xa3, 0xa3, 0x7f, 0x22, 0x51, 0x52, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4f, 0x9b, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0xa4, + 0xa5, 0xa5, 0xa6, 0x61, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x30, 0x32, + 0x25, 0x4d, 0x2b, 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x21, 0x23, 0x24, 0x26, 0x30, 0x33, 0x31, 0x4d, 0x91, 0x5b, 0xa2, + 0xa3, 0xa3, 0xa3, 0x5a, 0x21, 0x2e, 0x46, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x4f, 0x9b, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0xa7, 0xa8, 0x69, 0x66, 0xa9, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x21, 0x26, 0x25, 0x83, 0xaa, 0x2c, 0x25, + 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, 0x30, 0x35, + 0x2d, 0x2f, 0x37, 0x4a, 0x60, 0x85, 0xab, 0xac, 0xa3, 0xa3, 0xa3, 0x82, + 0x86, 0x36, 0x32, 0x3f, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4c, 0x99, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0xad, 0xa2, 0xa8, 0xae, 0xaf, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x21, 0x57, 0x77, 0x66, 0x34, 0x27, 0x22, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x23, 0x30, 0x31, 0xb0, 0x91, 0x7e, 0x90, 0x90, + 0x8b, 0x5b, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x5d, 0xb1, 0x36, 0x24, + 0x53, 0x9b, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x9b, 0x99, 0xad, + 0x64, 0x5c, 0x8b, 0xb1, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x73, 0x5d, + 0x82, 0x5c, 0xb2, 0x2a, 0x23, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, + 0x24, 0x2b, 0xb0, 0x8b, 0x5b, 0x76, 0x5b, 0x5b, 0x7b, 0xa2, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa8, 0x5e, 0x22, 0x36, 0x21, 0x3a, 0x99, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x4f, 0x3f, 0xb3, 0x7b, 0x7b, 0x85, 0x80, + 0x9f, 0x36, 0x36, 0x36, 0x21, 0xb4, 0x7e, 0x7b, 0x64, 0x64, 0xb5, 0x35, + 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x26, 0x31, 0xb6, 0x5b, + 0x64, 0xa2, 0xa2, 0xac, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0x66, 0xb7, 0x36, 0x36, 0x36, 0x2c, 0x4b, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x9a, 0x3f, 0xb8, 0x76, 0x76, 0x7a, 0x63, 0xb9, 0xba, 0x86, 0xba, + 0xbb, 0x90, 0x5b, 0x64, 0xa2, 0xa2, 0xbc, 0x2d, 0x27, 0x23, 0x21, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x21, 0x26, 0x2d, 0x91, 0x5b, 0x64, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa8, 0x83, 0xaf, + 0x36, 0x36, 0x36, 0x30, 0x44, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x9b, 0x9a, 0x3f, 0xbd, + 0x5b, 0x7b, 0xbe, 0x85, 0x7e, 0x90, 0x63, 0x90, 0x85, 0x5b, 0xa2, 0xa3, + 0xa3, 0xac, 0x5d, 0xb5, 0x39, 0x26, 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x26, 0x2d, 0xbf, 0xbe, 0x64, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa8, 0x88, 0x36, 0x36, 0x36, 0x36, + 0x2d, 0x9b, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x9b, 0x45, 0x3f, 0xc0, 0x6d, 0x7b, 0xab, 0xbe, + 0x7a, 0x8b, 0x8b, 0x7a, 0x5b, 0x64, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xc1, + 0x37, 0x35, 0x26, 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x26, 0x2e, 0xbf, 0x7a, + 0x7b, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa8, 0x72, 0x73, 0x36, 0x36, 0x36, 0x24, 0x52, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x46, 0x42, 0xb6, 0x7a, 0x7b, 0x64, 0x7b, 0x76, 0x5b, 0x5b, 0x76, + 0x7b, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xac, 0x64, 0xc1, 0x4d, 0x2c, 0x27, + 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x21, 0x25, 0x31, 0xc2, 0x8b, 0x7b, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa8, + 0x89, 0x9f, 0x36, 0x36, 0x32, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4b, 0x2f, 0x8f, + 0x7a, 0x7b, 0xa2, 0xac, 0xa2, 0x64, 0x64, 0xa2, 0xa2, 0xac, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0x5d, 0xc3, 0x2c, 0x26, 0x22, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, + 0x25, 0x31, 0xc2, 0x85, 0x7b, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x66, 0x57, 0x27, 0x4d, + 0x4b, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x99, 0x34, 0x9f, 0xb9, 0x7a, 0x7b, 0xa2, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa2, 0xc2, 0x32, 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x26, 0x2d, 0xc2, 0x85, + 0x7b, 0xac, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa8, 0x5f, 0x92, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x44, + 0x35, 0x36, 0xaf, 0xbb, 0x7a, 0x7b, 0xac, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xac, 0xa2, 0xc0, + 0x2b, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x23, 0x30, 0x2f, 0xb6, 0x8b, 0x7b, 0xac, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0x66, 0x89, 0x45, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x9b, 0x4e, 0x25, 0x36, 0x36, 0x61, 0xb9, + 0x6d, 0x64, 0xac, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xac, 0x7b, 0xbe, 0xc3, 0x32, 0x28, 0x21, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, + 0x33, 0xc4, 0x63, 0xbe, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0x72, 0x81, 0xc5, + 0x46, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x3f, 0x2c, 0x36, 0x36, 0x36, 0x36, 0xc6, 0x8f, 0x6d, 0x64, 0xac, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, + 0xab, 0x8b, 0xb0, 0x2c, 0x26, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x24, 0x35, 0x96, 0x75, 0xab, + 0xa2, 0xac, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xac, 0x7b, 0x81, 0xb9, 0x73, 0x3b, 0x44, 0x9b, + 0x48, 0x48, 0x48, 0x9b, 0x99, 0x43, 0x94, 0x2c, 0x21, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x73, 0xb9, 0x7a, 0x7b, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x64, 0x76, 0x7a, 0x91, 0xb5, 0x31, 0x30, + 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x21, 0x24, 0x39, 0x97, 0x75, 0xbe, 0x7b, 0x64, 0xa2, 0xa2, + 0xac, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0x7b, 0x7a, 0xc7, 0xc8, 0x36, 0x21, 0x26, 0x2b, 0x39, 0x33, 0x30, + 0x23, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x21, 0xc8, 0xbb, + 0x8b, 0x7b, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0x64, 0x64, + 0x76, 0x85, 0xbf, 0xb5, 0x34, 0x2b, 0x27, 0x28, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, + 0x33, 0xc9, 0x63, 0x7e, 0x7a, 0x6d, 0xbe, 0x5b, 0x76, 0x7b, 0x64, 0x64, + 0xa2, 0xac, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xac, 0x76, 0x85, 0xb9, + 0x79, 0x22, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x21, 0xca, 0xbb, 0x75, 0x76, 0xa2, 0xa3, + 0xa3, 0xa3, 0xac, 0xa2, 0x64, 0x76, 0xbe, 0x8b, 0xb6, 0xb5, 0x2f, 0x35, + 0x30, 0x24, 0x22, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, 0x27, 0x31, 0xcb, 0xc9, + 0xbb, 0x74, 0x63, 0x90, 0x7e, 0x75, 0x8b, 0x6d, 0xbe, 0x76, 0x64, 0xa2, + 0xac, 0xac, 0xac, 0xac, 0x64, 0x7a, 0x84, 0xcc, 0x79, 0x9f, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x21, 0xc8, 0xcc, 0x63, 0x6d, 0x7b, 0x64, 0xac, 0xa2, 0x64, 0x7b, + 0xbe, 0x75, 0x63, 0x96, 0x38, 0x39, 0x2a, 0x24, 0x23, 0x21, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x21, 0x28, 0x27, 0x35, 0x2d, 0x41, 0xb5, 0xc5, 0x8f, + 0xb9, 0xbb, 0xc7, 0x74, 0x84, 0x90, 0x85, 0x6d, 0x5b, 0x7b, 0x7b, 0xab, + 0x6d, 0x90, 0xb9, 0xcd, 0xca, 0x22, 0x36, 0x36, 0x28, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x25, 0x36, 0x36, 0x21, 0xb4, 0x80, + 0xc7, 0x7e, 0x6d, 0x76, 0xab, 0x76, 0x6d, 0x85, 0x63, 0xb9, 0xb5, 0x34, + 0x33, 0x26, 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x21, 0x23, 0x24, 0x27, 0x2a, 0x35, 0x2e, 0x2f, 0x41, 0xce, 0xcf, 0x6c, + 0x80, 0xcc, 0xb9, 0x74, 0x84, 0x90, 0x75, 0x7e, 0x74, 0x8f, 0xcd, 0x79, + 0xc6, 0x2b, 0x9d, 0x41, 0x2f, 0x34, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x34, 0x2f, 0x38, 0x4d, 0x37, 0xd0, 0xd1, 0x8f, 0x74, 0x63, 0x7e, + 0x75, 0x7e, 0x63, 0xc7, 0x88, 0xc4, 0x31, 0x2a, 0x24, 0x22, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x22, + 0x23, 0x24, 0x26, 0x30, 0x33, 0x39, 0x2e, 0x51, 0x41, 0xb2, 0x6c, 0xd1, + 0x80, 0xcc, 0xcc, 0xcc, 0xd2, 0xd1, 0xb7, 0xd3, 0x41, 0x34, 0x35, 0x32, + 0x30, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x30, 0x2a, + 0x2b, 0x34, 0xd4, 0xca, 0xd5, 0x8f, 0xbb, 0xc7, 0xc7, 0xbb, 0xcc, 0x6c, + 0x41, 0x39, 0x27, 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, + 0x28, 0x24, 0x26, 0x2a, 0x33, 0x2c, 0x2f, 0x41, 0xd6, 0xb7, 0x79, 0x79, + 0x79, 0xca, 0xd7, 0x51, 0x39, 0x30, 0x24, 0x23, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x24, 0x2a, 0x31, 0xd8, + 0xc8, 0x79, 0xd1, 0x80, 0xd5, 0xba, 0xd9, 0x2f, 0x35, 0x26, 0x23, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x22, 0x23, + 0x28, 0x25, 0x30, 0x2b, 0x31, 0x2f, 0xd4, 0xd8, 0xd8, 0x2f, 0x2e, 0x33, + 0x26, 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, 0x27, 0x35, 0x34, 0xd8, 0xd8, 0xd8, + 0xda, 0xd4, 0x2e, 0x33, 0x25, 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x23, 0x28, + 0x26, 0x30, 0x32, 0x2b, 0x33, 0x2a, 0x26, 0x28, 0x22, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x21, 0x23, 0x25, 0x30, 0x33, 0x35, 0x35, 0x2b, 0x2a, 0x26, 0x28, + 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x23, 0x28, + 0x28, 0x23, 0x22, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, + 0x23, 0x28, 0x24, 0x24, 0x28, 0x23, 0x22, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20 +}; + +static const unsigned char logo_linux_clut224_clut[] __initdata = { + 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x0a, 0x0a, 0x0a, 0x0e, 0x0e, 0x0e, + 0x16, 0x16, 0x16, 0x1a, 0x1a, 0x1a, 0x1e, 0x1e, 0x1e, 0x22, 0x22, 0x22, + 0x12, 0x12, 0x12, 0x00, 0x00, 0x01, 0x2a, 0x2a, 0x2a, 0x36, 0x36, 0x36, + 0x42, 0x42, 0x42, 0x4e, 0x4e, 0x4e, 0x4a, 0x4a, 0x4a, 0x56, 0x56, 0x56, + 0x26, 0x26, 0x26, 0x46, 0x46, 0x46, 0x2e, 0x2e, 0x2e, 0x32, 0x32, 0x32, + 0x52, 0x52, 0x52, 0x3a, 0x3a, 0x3a, 0x02, 0x02, 0x06, 0x65, 0x65, 0x65, + 0x5e, 0x5e, 0x5e, 0x3e, 0x3e, 0x3e, 0x74, 0x74, 0x74, 0x8a, 0x8a, 0x8a, + 0xa2, 0xa2, 0xa2, 0x9a, 0x9a, 0x9a, 0x86, 0x86, 0x86, 0xc6, 0xc6, 0xc6, + 0xc3, 0xc3, 0xc3, 0x65, 0x62, 0x59, 0xbb, 0xbb, 0xbb, 0xd2, 0xd2, 0xd2, + 0xda, 0xda, 0xda, 0xd6, 0xd6, 0xd6, 0xe2, 0xe2, 0xe2, 0xf6, 0xf6, 0xf6, + 0xfd, 0xfd, 0xfd, 0xae, 0xae, 0xae, 0x7b, 0x7b, 0x7b, 0xdd, 0xdd, 0xdd, + 0xea, 0xea, 0xea, 0x6a, 0x6a, 0x6a, 0xaa, 0xaa, 0xaa, 0xe7, 0xe7, 0xe7, + 0xbe, 0xbe, 0xbe, 0x5a, 0x5a, 0x5a, 0xee, 0xee, 0xee, 0x9e, 0x9e, 0x9e, + 0x95, 0x95, 0x95, 0x80, 0x80, 0x80, 0x76, 0x62, 0x2e, 0x79, 0x5c, 0x08, + 0x62, 0x4e, 0x0a, 0x36, 0x26, 0x06, 0x9a, 0x72, 0x0a, 0xe2, 0xaa, 0x0b, + 0xec, 0xba, 0x0b, 0xe1, 0xaf, 0x0f, 0xb8, 0x90, 0x0c, 0xd7, 0xae, 0x0f, + 0xaf, 0x92, 0x3d, 0x25, 0x1a, 0x09, 0xbc, 0xa4, 0x73, 0xc0, 0x85, 0x09, + 0xef, 0xb6, 0x0d, 0xea, 0xbe, 0x0a, 0xe8, 0xc3, 0x10, 0xe8, 0xc8, 0x1e, + 0xf5, 0xcf, 0x2d, 0xf1, 0xd0, 0x13, 0xda, 0xc2, 0x86, 0xd3, 0xce, 0xba, + 0x79, 0x57, 0x19, 0xdb, 0xa2, 0x0a, 0xf4, 0xd6, 0x36, 0xf6, 0xda, 0x3c, + 0xf6, 0xda, 0x26, 0xf6, 0xd7, 0x14, 0xe2, 0xb8, 0x0d, 0x3d, 0x2a, 0x06, + 0xb4, 0x7b, 0x07, 0xce, 0x91, 0x0a, 0xe6, 0xae, 0x0b, 0xee, 0xca, 0x0f, + 0xf6, 0xda, 0x4a, 0x68, 0x45, 0x06, 0xd8, 0x9e, 0x0a, 0xec, 0xb2, 0x0c, + 0xf5, 0xd7, 0x2b, 0xc6, 0x9b, 0x0a, 0xc8, 0x90, 0x0b, 0x9c, 0x76, 0x0a, + 0x89, 0x5c, 0x06, 0xd2, 0xa2, 0x0a, 0xee, 0xbe, 0x0a, 0xcb, 0xa6, 0x11, + 0xb9, 0x85, 0x0b, 0xd2, 0x96, 0x0a, 0x66, 0x4e, 0x0a, 0x5e, 0x46, 0x1e, + 0x8b, 0x66, 0x0f, 0xbe, 0x92, 0x0d, 0xa8, 0x7a, 0x0a, 0xd5, 0x9a, 0x0b, + 0xca, 0x96, 0x22, 0xb6, 0x9d, 0x6a, 0xae, 0x9a, 0x72, 0x9c, 0x6b, 0x0b, + 0xc5, 0x8a, 0x0b, 0xbe, 0x8e, 0x22, 0xbe, 0xb2, 0x90, 0xca, 0xca, 0xca, + 0x90, 0x90, 0x90, 0xb2, 0xa6, 0x92, 0x9a, 0x79, 0x3c, 0xa8, 0x7c, 0x2c, + 0xb6, 0xb6, 0xb6, 0xf2, 0xf2, 0xf2, 0xce, 0xce, 0xce, 0xfa, 0xfa, 0xfa, + 0xb2, 0xb2, 0xb2, 0x6e, 0x6e, 0x6e, 0xa6, 0xa6, 0xa6, 0x12, 0x0e, 0x06, + 0x4a, 0x36, 0x0e, 0x8e, 0x86, 0x6a, 0xf2, 0xba, 0x0e, 0xf6, 0xbe, 0x0e, + 0xf6, 0xe6, 0xbe, 0xee, 0xcc, 0x5b, 0xb5, 0x8e, 0x2c, 0xe4, 0xb8, 0x3e, + 0xf1, 0xc4, 0x0e, 0x26, 0x1e, 0x0a, 0x9a, 0x8e, 0x5a, 0xea, 0xae, 0x0d, + 0xf6, 0xba, 0x0e, 0xe0, 0xb2, 0x3e, 0xd2, 0xa6, 0x0a, 0x16, 0x12, 0x06, + 0x9a, 0x7a, 0x2e, 0x2e, 0x20, 0x06, 0x70, 0x64, 0x4e, 0xd6, 0xaa, 0x36, + 0x46, 0x2f, 0x06, 0x7c, 0x70, 0x58, 0xb4, 0x85, 0x24, 0x62, 0x46, 0x06, + 0xd6, 0xa6, 0x3a, 0xa3, 0x6e, 0x08, 0x74, 0x51, 0x08, 0xa7, 0x72, 0x07, + 0xa2, 0x92, 0x5e, 0xca, 0xa2, 0x45, 0xe0, 0xa6, 0x0a, 0xae, 0x87, 0x32, + 0xbe, 0x96, 0x2e, 0xce, 0xa2, 0x2a, 0xa3, 0x85, 0x43, 0x8e, 0x7a, 0x48, + 0x6d, 0x6a, 0x5f, 0x8e, 0x6e, 0x2e, 0x32, 0x22, 0x06, 0xaf, 0x76, 0x06, + 0x50, 0x36, 0x07, 0x9e, 0x76, 0x24, 0x5b, 0x3c, 0x06, 0x8a, 0x6e, 0x32, + 0x98, 0x63, 0x06, 0x82, 0x53, 0x06, 0x7a, 0x66, 0x46, 0x82, 0x62, 0x2e, + 0x56, 0x42, 0x22, 0x7c, 0x50, 0x06, 0x8a, 0x56, 0x06, 0x56, 0x42, 0x1e, + 0x5e, 0x56, 0x4c, 0x86, 0x56, 0x06, 0x6a, 0x56, 0x3a, 0x52, 0x3e, 0x22, + 0x59, 0x51, 0x42, 0x64, 0x52, 0x34, 0x5e, 0x56, 0x42 +}; + +const struct linux_logo logo_linux_clut224 __initdata = { + .type = LINUX_LOGO_CLUT224, + .width = 80, + .height = 80, + .clutsize = 187, + .clut = logo_linux_clut224_clut, + .data = logo_linux_clut224_data +}; + diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/fs/binfmt_elf.c linux-2.6.0-test1-ac1/fs/binfmt_elf.c --- linux-2.6.0-test1/fs/binfmt_elf.c 2003-07-14 14:11:56.000000000 +0100 +++ linux-2.6.0-test1-ac1/fs/binfmt_elf.c 2003-07-14 20:26:52.000000000 +0100 @@ -460,6 +460,7 @@ struct elfhdr interp_elf_ex; struct exec interp_ex; char passed_fileno[6]; + struct files_struct *files, *ftmp; /* Get the exec-header */ elf_ex = *((struct elfhdr *) bprm->buf); @@ -492,9 +493,17 @@ if (retval < 0) goto out_free_ph; + files = current->files; /* Refcounted so ok */ + if(unshare_files() < 0) + goto out_free_ph; + + + /* exec will make our files private anyway, but for the a.out + loader stuff we need to do it earlier */ + retval = get_unused_fd(); if (retval < 0) - goto out_free_ph; + goto out_free_fh; get_file(bprm->file); fd_install(elf_exec_fileno = retval, bprm->file); @@ -628,6 +637,8 @@ current->mm->mmap = NULL; current->flags &= ~PF_FORKNOEXEC; + put_files_struct(files); + /* Do this immediately, since STACK_TOP as used in setup_arg_pages may depend on the personality. */ SET_PERSONALITY(elf_ex, ibcs2_interpreter); @@ -825,6 +836,10 @@ kfree(elf_interpreter); out_free_file: sys_close(elf_exec_fileno); +out_free_fh: + ftmp = current->files; + current->files = files; + put_files_struct(ftmp); out_free_ph: kfree(elf_phdata); goto out; diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/fs/exec.c linux-2.6.0-test1-ac1/fs/exec.c --- linux-2.6.0-test1/fs/exec.c 2003-07-14 14:11:56.000000000 +0100 +++ linux-2.6.0-test1-ac1/fs/exec.c 2003-07-14 20:09:26.000000000 +0100 @@ -758,6 +758,7 @@ { char * name; int i, ch, retval; + struct files_struct * files; /* * Make sure we have a private signal table and that @@ -768,6 +769,18 @@ goto out; /* + * Make sure we have private file handles. Ask the + * fork helper to do the work for us and the exit + * helper to do the cleanup of the old one. + */ + + files = current->files; /* refcounted so safe to hold */ + retval = unshare_files(); + if(retval) + goto out; + put_files_struct(files); + + /* * Release all of the old mmap stuff */ retval = exec_mmap(bprm->mm); diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/fs/proc/base.c linux-2.6.0-test1-ac1/fs/proc/base.c --- linux-2.6.0-test1/fs/proc/base.c 2003-07-14 14:11:56.000000000 +0100 +++ linux-2.6.0-test1-ac1/fs/proc/base.c 2003-07-14 19:39:33.000000000 +0100 @@ -218,16 +218,59 @@ return result; } +#define MAY_PTRACE(p) \ +(p==current||(p->parent==current&&(p->ptrace & PT_PTRACED)&&p->state==TASK_STOPPED&&security_ptrace(current,p)==0)) + +static int task_dumpable(struct task_struct *task) +{ + int dumpable = 0; + struct mm_struct *mm; + + task_lock(task); + mm = task->mm; + if (mm) + dumpable = mm->dumpable; + task_unlock(task); + return dumpable; +} + +static int may_ptrace_attach(struct task_struct *task) +{ + int retval = 0; + + task_lock(task); + + if (((current->uid != task->euid) || + (current->uid != task->suid) || + (current->uid != task->uid) || + (current->gid != task->egid) || + (current->gid != task->sgid) || + (!cap_issubset(task->cap_permitted, current->cap_permitted)) || + (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE)) + goto out; + rmb(); + if ((!task->mm || !task->mm->dumpable) && !capable(CAP_SYS_PTRACE)) + goto out; + + retval = 1; + +out: + task_unlock(task); + return retval; +} + static int proc_pid_environ(struct task_struct *task, char * buffer) { int res = 0; struct mm_struct *mm = get_task_mm(task); if (mm) { - int len = mm->env_end - mm->env_start; + unsigned long len = mm->env_end - mm->env_start; if (len > PAGE_SIZE) len = PAGE_SIZE; res = access_process_vm(task, mm->env_start, buffer, len, 0); mmput(mm); + if (!may_ptrace_attach(task)) + return -ESRCH; } return res; } @@ -443,8 +486,6 @@ .read = proc_info_read, }; -#define MAY_PTRACE(p) \ -(p==current||(p->parent==current&&(p->ptrace & PT_PTRACED)&&p->state==TASK_STOPPED&&security_ptrace(current,p)==0)) static int mem_open(struct inode* inode, struct file* file) @@ -462,7 +503,7 @@ int ret = -ESRCH; struct mm_struct *mm; - if (!MAY_PTRACE(task)) + if (!MAY_PTRACE(task) || !may_ptrace_attach(task)) goto out; ret = -ENOMEM; @@ -488,7 +529,7 @@ this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; retval = access_process_vm(task, src, page, this_len, 0); - if (!retval) { + if (!retval || !MAY_PTRACE(task) || !may_ptrace_attach(task)) { if (!ret) ret = -EIO; break; @@ -801,20 +842,6 @@ /* building an inode */ -static int task_dumpable(struct task_struct *task) -{ - int dumpable = 0; - struct mm_struct *mm; - - task_lock(task); - mm = task->mm; - if (mm) - dumpable = mm->dumpable; - task_unlock(task); - return dumpable; -} - - static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *task, int ino) { struct inode * inode; diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/fs/proc/proc_misc.c linux-2.6.0-test1-ac1/fs/proc/proc_misc.c --- linux-2.6.0-test1/fs/proc/proc_misc.c 2003-07-10 21:05:28.000000000 +0100 +++ linux-2.6.0-test1-ac1/fs/proc/proc_misc.c 2003-07-14 16:23:02.000000000 +0100 @@ -536,7 +536,8 @@ buf++; p++; count--; read++; } pnt = (char *)prof_buffer + p - sizeof(unsigned int); - copy_to_user(buf,(void *)pnt,count); + if(copy_to_user(buf,(void *)pnt,count)) + return -EFAULT; read += count; *ppos += read; return read; diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/include/asm-i386/asm_offsets.h linux-2.6.0-test1-ac1/include/asm-i386/asm_offsets.h --- linux-2.6.0-test1/include/asm-i386/asm_offsets.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/include/asm-i386/asm_offsets.h 2003-07-11 12:43:39.000000000 +0100 @@ -0,0 +1,22 @@ +#ifndef __ASM_OFFSETS_H__ +#define __ASM_OFFSETS_H__ +/* + * DO NOT MODIFY. + * + * This file was generated by arch/i386/Makefile + * + */ + +#define SIGCONTEXT_eax 44 /* offsetof (struct sigcontext, eax) */ +#define SIGCONTEXT_ebx 32 /* offsetof (struct sigcontext, ebx) */ +#define SIGCONTEXT_ecx 40 /* offsetof (struct sigcontext, ecx) */ +#define SIGCONTEXT_edx 36 /* offsetof (struct sigcontext, edx) */ +#define SIGCONTEXT_esi 20 /* offsetof (struct sigcontext, esi) */ +#define SIGCONTEXT_edi 16 /* offsetof (struct sigcontext, edi) */ +#define SIGCONTEXT_ebp 24 /* offsetof (struct sigcontext, ebp) */ +#define SIGCONTEXT_esp 28 /* offsetof (struct sigcontext, esp) */ +#define SIGCONTEXT_eip 56 /* offsetof (struct sigcontext, eip) */ + +#define RT_SIGFRAME_sigcontext 164 /* offsetof (struct rt_sigframe, uc.uc_mcontext) */ + +#endif diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/include/linux/dm-ioctl.h linux-2.6.0-test1-ac1/include/linux/dm-ioctl.h --- linux-2.6.0-test1/include/linux/dm-ioctl.h 2003-07-10 21:04:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/include/linux/dm-ioctl.h 2003-07-14 14:50:50.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001 Sistina Software (UK) Limited. + * Copyright (C) 2003 Sistina Software (UK) Limited. * * This file is released under the LGPL. */ @@ -7,143 +7,12 @@ #ifndef _LINUX_DM_IOCTL_H #define _LINUX_DM_IOCTL_H -#include +#include -#define DM_DIR "mapper" /* Slashes not supported */ -#define DM_MAX_TYPE_NAME 16 -#define DM_NAME_LEN 128 -#define DM_UUID_LEN 129 +#ifdef CONFIG_DM_IOCTL_V4 +#include "dm-ioctl-v4.h" +#else +#include "dm-ioctl-v1.h" +#endif -/* - * Implements a traditional ioctl interface to the device mapper. - */ - -/* - * All ioctl arguments consist of a single chunk of memory, with - * this structure at the start. If a uuid is specified any - * lookup (eg. for a DM_INFO) will be done on that, *not* the - * name. - */ -struct dm_ioctl { - /* - * The version number is made up of three parts: - * major - no backward or forward compatibility, - * minor - only backwards compatible, - * patch - both backwards and forwards compatible. - * - * All clients of the ioctl interface should fill in the - * version number of the interface that they were - * compiled with. - * - * All recognised ioctl commands (ie. those that don't - * return -ENOTTY) fill out this field, even if the - * command failed. - */ - uint32_t version[3]; /* in/out */ - uint32_t data_size; /* total size of data passed in - * including this struct */ - - uint32_t data_start; /* offset to start of data - * relative to start of this struct */ - - uint32_t target_count; /* in/out */ - uint32_t open_count; /* out */ - uint32_t flags; /* in/out */ - - __kernel_dev_t dev; /* in/out */ - - char name[DM_NAME_LEN]; /* device name */ - char uuid[DM_UUID_LEN]; /* unique identifier for - * the block device */ -}; - -/* - * Used to specify tables. These structures appear after the - * dm_ioctl. - */ -struct dm_target_spec { - int32_t status; /* used when reading from kernel only */ - uint64_t sector_start; - uint32_t length; - - /* - * Offset in bytes (from the start of this struct) to - * next target_spec. - */ - uint32_t next; - - char target_type[DM_MAX_TYPE_NAME]; - - /* - * Parameter string starts immediately after this object. - * Be careful to add padding after string to ensure correct - * alignment of subsequent dm_target_spec. - */ -}; - -/* - * Used to retrieve the target dependencies. - */ -struct dm_target_deps { - uint32_t count; - - __kernel_dev_t dev[0]; /* out */ -}; - -/* - * If you change this make sure you make the corresponding change - * to dm-ioctl.c:lookup_ioctl() - */ -enum { - /* Top level cmds */ - DM_VERSION_CMD = 0, - DM_REMOVE_ALL_CMD, - - /* device level cmds */ - DM_DEV_CREATE_CMD, - DM_DEV_REMOVE_CMD, - DM_DEV_RELOAD_CMD, - DM_DEV_RENAME_CMD, - DM_DEV_SUSPEND_CMD, - DM_DEV_DEPS_CMD, - DM_DEV_STATUS_CMD, - - /* target level cmds */ - DM_TARGET_STATUS_CMD, - DM_TARGET_WAIT_CMD -}; - -#define DM_IOCTL 0xfd - -#define DM_VERSION _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl) -#define DM_REMOVE_ALL _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl) - -#define DM_DEV_CREATE _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl) -#define DM_DEV_REMOVE _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl) -#define DM_DEV_RELOAD _IOWR(DM_IOCTL, DM_DEV_RELOAD_CMD, struct dm_ioctl) -#define DM_DEV_SUSPEND _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl) -#define DM_DEV_RENAME _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl) -#define DM_DEV_DEPS _IOWR(DM_IOCTL, DM_DEV_DEPS_CMD, struct dm_ioctl) -#define DM_DEV_STATUS _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl) - -#define DM_TARGET_STATUS _IOWR(DM_IOCTL, DM_TARGET_STATUS_CMD, struct dm_ioctl) -#define DM_TARGET_WAIT _IOWR(DM_IOCTL, DM_TARGET_WAIT_CMD, struct dm_ioctl) - -#define DM_VERSION_MAJOR 1 -#define DM_VERSION_MINOR 0 -#define DM_VERSION_PATCHLEVEL 6 -#define DM_VERSION_EXTRA "-ioctl (2002-10-15)" - -/* Status bits */ -#define DM_READONLY_FLAG 0x00000001 -#define DM_SUSPEND_FLAG 0x00000002 -#define DM_EXISTS_FLAG 0x00000004 -#define DM_PERSISTENT_DEV_FLAG 0x00000008 - -/* - * Flag passed into ioctl STATUS command to get table information - * rather than current status. - */ -#define DM_STATUS_TABLE_FLAG 0x00000010 - -#endif /* _LINUX_DM_IOCTL_H */ +#endif diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/include/linux/dm-ioctl-v1.h linux-2.6.0-test1-ac1/include/linux/dm-ioctl-v1.h --- linux-2.6.0-test1/include/linux/dm-ioctl-v1.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/include/linux/dm-ioctl-v1.h 2003-07-14 14:50:50.000000000 +0100 @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#ifndef _LINUX_DM_IOCTL_V1_H +#define _LINUX_DM_IOCTL_V1_H + +#include + +#define DM_DIR "mapper" /* Slashes not supported */ +#define DM_MAX_TYPE_NAME 16 +#define DM_NAME_LEN 128 +#define DM_UUID_LEN 129 + +/* + * Implements a traditional ioctl interface to the device mapper. + */ + +/* + * All ioctl arguments consist of a single chunk of memory, with + * this structure at the start. If a uuid is specified any + * lookup (eg. for a DM_INFO) will be done on that, *not* the + * name. + */ +struct dm_ioctl { + /* + * The version number is made up of three parts: + * major - no backward or forward compatibility, + * minor - only backwards compatible, + * patch - both backwards and forwards compatible. + * + * All clients of the ioctl interface should fill in the + * version number of the interface that they were + * compiled with. + * + * All recognised ioctl commands (ie. those that don't + * return -ENOTTY) fill out this field, even if the + * command failed. + */ + uint32_t version[3]; /* in/out */ + uint32_t data_size; /* total size of data passed in + * including this struct */ + + uint32_t data_start; /* offset to start of data + * relative to start of this struct */ + + uint32_t target_count; /* in/out */ + uint32_t open_count; /* out */ + uint32_t flags; /* in/out */ + + __kernel_dev_t dev; /* in/out */ + + char name[DM_NAME_LEN]; /* device name */ + char uuid[DM_UUID_LEN]; /* unique identifier for + * the block device */ +}; + +/* + * Used to specify tables. These structures appear after the + * dm_ioctl. + */ +struct dm_target_spec { + int32_t status; /* used when reading from kernel only */ + uint64_t sector_start; + uint32_t length; + + /* + * Offset in bytes (from the start of this struct) to + * next target_spec. + */ + uint32_t next; + + char target_type[DM_MAX_TYPE_NAME]; + + /* + * Parameter string starts immediately after this object. + * Be careful to add padding after string to ensure correct + * alignment of subsequent dm_target_spec. + */ +}; + +/* + * Used to retrieve the target dependencies. + */ +struct dm_target_deps { + uint32_t count; + + __kernel_dev_t dev[0]; /* out */ +}; + +/* + * If you change this make sure you make the corresponding change + * to dm-ioctl.c:lookup_ioctl() + */ +enum { + /* Top level cmds */ + DM_VERSION_CMD = 0, + DM_REMOVE_ALL_CMD, + + /* device level cmds */ + DM_DEV_CREATE_CMD, + DM_DEV_REMOVE_CMD, + DM_DEV_RELOAD_CMD, + DM_DEV_RENAME_CMD, + DM_DEV_SUSPEND_CMD, + DM_DEV_DEPS_CMD, + DM_DEV_STATUS_CMD, + + /* target level cmds */ + DM_TARGET_STATUS_CMD, + DM_TARGET_WAIT_CMD +}; + +#define DM_IOCTL 0xfd + +#define DM_VERSION _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl) +#define DM_REMOVE_ALL _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl) + +#define DM_DEV_CREATE _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl) +#define DM_DEV_REMOVE _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl) +#define DM_DEV_RELOAD _IOWR(DM_IOCTL, DM_DEV_RELOAD_CMD, struct dm_ioctl) +#define DM_DEV_SUSPEND _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl) +#define DM_DEV_RENAME _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl) +#define DM_DEV_DEPS _IOWR(DM_IOCTL, DM_DEV_DEPS_CMD, struct dm_ioctl) +#define DM_DEV_STATUS _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl) + +#define DM_TARGET_STATUS _IOWR(DM_IOCTL, DM_TARGET_STATUS_CMD, struct dm_ioctl) +#define DM_TARGET_WAIT _IOWR(DM_IOCTL, DM_TARGET_WAIT_CMD, struct dm_ioctl) + +#define DM_VERSION_MAJOR 1 +#define DM_VERSION_MINOR 0 +#define DM_VERSION_PATCHLEVEL 6 +#define DM_VERSION_EXTRA "-ioctl (2002-10-15)" + +/* Status bits */ +#define DM_READONLY_FLAG 0x00000001 +#define DM_SUSPEND_FLAG 0x00000002 +#define DM_EXISTS_FLAG 0x00000004 +#define DM_PERSISTENT_DEV_FLAG 0x00000008 + +/* + * Flag passed into ioctl STATUS command to get table information + * rather than current status. + */ +#define DM_STATUS_TABLE_FLAG 0x00000010 + +#endif /* _LINUX_DM_IOCTL_H */ diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/include/linux/dm-ioctl-v4.h linux-2.6.0-test1-ac1/include/linux/dm-ioctl-v4.h --- linux-2.6.0-test1/include/linux/dm-ioctl-v4.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/include/linux/dm-ioctl-v4.h 2003-07-14 14:50:50.000000000 +0100 @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2001 - 2003 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#ifndef _LINUX_DM_IOCTL_V4_H +#define _LINUX_DM_IOCTL_V4_H + +#include + +#define DM_DIR "mapper" /* Slashes not supported */ +#define DM_MAX_TYPE_NAME 16 +#define DM_NAME_LEN 128 +#define DM_UUID_LEN 129 + +/* + * A traditional ioctl interface for the device mapper. + * + * Each device can have two tables associated with it, an + * 'active' table which is the one currently used by io passing + * through the device, and an 'inactive' one which is a table + * that is being prepared as a replacement for the 'active' one. + * + * DM_VERSION: + * Just get the version information for the ioctl interface. + * + * DM_REMOVE_ALL: + * Remove all dm devices, destroy all tables. Only really used + * for debug. + * + * DM_LIST_DEVICES: + * Get a list of all the dm device names. + * + * DM_DEV_CREATE: + * Create a new device, neither the 'active' or 'inactive' table + * slots will be filled. The device will be in suspended state + * after creation, however any io to the device will get errored + * since it will be out-of-bounds. + * + * DM_DEV_REMOVE: + * Remove a device, destroy any tables. + * + * DM_DEV_RENAME: + * Rename a device. + * + * DM_SUSPEND: + * This performs both suspend and resume, depending which flag is + * passed in. + * Suspend: This command will not return until all pending io to + * the device has completed. Further io will be deferred until + * the device is resumed. + * Resume: It is no longer an error to issue this command on an + * unsuspended device. If a table is present in the 'inactive' + * slot, it will be moved to the active slot, then the old table + * from the active slot will be _destroyed_. Finally the device + * is resumed. + * + * DM_DEV_STATUS: + * Retrieves the status for the table in the 'active' slot. + * + * DM_DEV_WAIT: + * Wait for a significant event to occur to the device. This + * could either be caused by an event triggered by one of the + * targets of the table in the 'active' slot, or a table change. + * + * DM_TABLE_LOAD: + * Load a table into the 'inactive' slot for the device. The + * device does _not_ need to be suspended prior to this command. + * + * DM_TABLE_CLEAR: + * Destroy any table in the 'inactive' slot (ie. abort). + * + * DM_TABLE_DEPS: + * Return a set of device dependencies for the 'active' table. + * + * DM_TABLE_STATUS: + * Return the targets status for the 'active' table. + */ + +/* + * All ioctl arguments consist of a single chunk of memory, with + * this structure at the start. If a uuid is specified any + * lookup (eg. for a DM_INFO) will be done on that, *not* the + * name. + */ +struct dm_ioctl { + /* + * The version number is made up of three parts: + * major - no backward or forward compatibility, + * minor - only backwards compatible, + * patch - both backwards and forwards compatible. + * + * All clients of the ioctl interface should fill in the + * version number of the interface that they were + * compiled with. + * + * All recognised ioctl commands (ie. those that don't + * return -ENOTTY) fill out this field, even if the + * command failed. + */ + uint32_t version[3]; /* in/out */ + uint32_t data_size; /* total size of data passed in + * including this struct */ + + uint32_t data_start; /* offset to start of data + * relative to start of this struct */ + + uint32_t target_count; /* in/out */ + int32_t open_count; /* out */ + uint32_t flags; /* in/out */ + uint32_t event_nr; /* in/out */ + uint32_t padding; + + uint64_t dev; /* in/out */ + + char name[DM_NAME_LEN]; /* device name */ + char uuid[DM_UUID_LEN]; /* unique identifier for + * the block device */ +}; + +/* + * Used to specify tables. These structures appear after the + * dm_ioctl. + */ +struct dm_target_spec { + uint64_t sector_start; + uint64_t length; + int32_t status; /* used when reading from kernel only */ + + /* + * Offset in bytes (from the start of this struct) to + * next target_spec. + */ + uint32_t next; + + char target_type[DM_MAX_TYPE_NAME]; + + /* + * Parameter string starts immediately after this object. + * Be careful to add padding after string to ensure correct + * alignment of subsequent dm_target_spec. + */ +}; + +/* + * Used to retrieve the target dependencies. + */ +struct dm_target_deps { + uint32_t count; /* Array size */ + uint32_t padding; /* unused */ + uint64_t dev[0]; /* out */ +}; + +/* + * Used to get a list of all dm devices. + */ +struct dm_name_list { + uint64_t dev; + uint32_t next; /* offset to the next record from + the _start_ of this */ + char name[0]; +}; + +/* + * If you change this make sure you make the corresponding change + * to dm-ioctl.c:lookup_ioctl() + */ +enum { + /* Top level cmds */ + DM_VERSION_CMD = 0, + DM_REMOVE_ALL_CMD, + DM_LIST_DEVICES_CMD, + + /* device level cmds */ + DM_DEV_CREATE_CMD, + DM_DEV_REMOVE_CMD, + DM_DEV_RENAME_CMD, + DM_DEV_SUSPEND_CMD, + DM_DEV_STATUS_CMD, + DM_DEV_WAIT_CMD, + + /* Table level cmds */ + DM_TABLE_LOAD_CMD, + DM_TABLE_CLEAR_CMD, + DM_TABLE_DEPS_CMD, + DM_TABLE_STATUS_CMD, +}; + +#define DM_IOCTL 0xfd + +#define DM_VERSION _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl) +#define DM_REMOVE_ALL _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl) +#define DM_LIST_DEVICES _IOWR(DM_IOCTL, DM_LIST_DEVICES_CMD, struct dm_ioctl) + +#define DM_DEV_CREATE _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl) +#define DM_DEV_REMOVE _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl) +#define DM_DEV_RENAME _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl) +#define DM_DEV_SUSPEND _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl) +#define DM_DEV_STATUS _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl) +#define DM_DEV_WAIT _IOWR(DM_IOCTL, DM_DEV_WAIT_CMD, struct dm_ioctl) + +#define DM_TABLE_LOAD _IOWR(DM_IOCTL, DM_TABLE_LOAD_CMD, struct dm_ioctl) +#define DM_TABLE_CLEAR _IOWR(DM_IOCTL, DM_TABLE_CLEAR_CMD, struct dm_ioctl) +#define DM_TABLE_DEPS _IOWR(DM_IOCTL, DM_TABLE_DEPS_CMD, struct dm_ioctl) +#define DM_TABLE_STATUS _IOWR(DM_IOCTL, DM_TABLE_STATUS_CMD, struct dm_ioctl) + +#define DM_VERSION_MAJOR 4 +#define DM_VERSION_MINOR 0 +#define DM_VERSION_PATCHLEVEL 0 +#define DM_VERSION_EXTRA "-ioctl (2003-06-04)" + +/* Status bits */ +#define DM_READONLY_FLAG (1 << 0) /* In/Out */ +#define DM_SUSPEND_FLAG (1 << 1) /* In/Out */ +#define DM_PERSISTENT_DEV_FLAG (1 << 3) /* In */ + +/* + * Flag passed into ioctl STATUS command to get table information + * rather than current status. + */ +#define DM_STATUS_TABLE_FLAG (1 << 4) /* In */ + +/* + * Flags that indicate whether a table is present in either of + * the two table slots that a device has. + */ +#define DM_ACTIVE_PRESENT_FLAG (1 << 5) /* Out */ +#define DM_INACTIVE_PRESENT_FLAG (1 << 6) /* Out */ + +/* + * Indicates that the buffer passed in wasn't big enough for the + * results. + */ +#define DM_BUFFER_FULL_FLAG (1 << 8) /* Out */ + +#endif /* _LINUX_DM_IOCTL_H */ diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/include/linux/fs.h linux-2.6.0-test1-ac1/include/linux/fs.h --- linux-2.6.0-test1/include/linux/fs.h 2003-07-14 14:11:56.000000000 +0100 +++ linux-2.6.0-test1-ac1/include/linux/fs.h 2003-07-14 16:37:16.000000000 +0100 @@ -1384,5 +1384,9 @@ return res; } +/* kernel/fork.c */ +extern int unshare_files(void); + + #endif /* __KERNEL__ */ #endif /* _LINUX_FS_H */ diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/include/linux/sched.h linux-2.6.0-test1-ac1/include/linux/sched.h --- linux-2.6.0-test1/include/linux/sched.h 2003-07-10 21:04:59.000000000 +0100 +++ linux-2.6.0-test1-ac1/include/linux/sched.h 2003-07-14 17:19:33.000000000 +0100 @@ -118,6 +118,8 @@ #define set_current_state(state_value) \ set_mb(current->state, (state_value)) +#define is_dumpable(tsk) ((tsk)->task_dumpable && (tsk)->mm && (tsk)->mm->dumpable) + /* * Scheduling policies */ diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/include/pcmcia/ss.h linux-2.6.0-test1-ac1/include/pcmcia/ss.h --- linux-2.6.0-test1/include/pcmcia/ss.h 2003-07-14 14:11:57.000000000 +0100 +++ linux-2.6.0-test1-ac1/include/pcmcia/ss.h 2003-07-14 15:14:26.000000000 +0100 @@ -51,6 +51,7 @@ #define SS_3VCARD 0x1000 #define SS_XVCARD 0x2000 #define SS_PENDING 0x4000 +#define SS_ZVCARD 0x8000 /* InquireSocket capabilities */ #define SS_CAP_PAGE_REGS 0x0001 @@ -209,6 +210,10 @@ /* socket operations */ struct pccard_operations * ops; + /* Zoom video behaviour is so chip specific its not worth adding + this to _ops */ + void (*zoom_video)(struct pcmcia_socket *, int); + /* state thread */ struct semaphore skt_sem; /* protects socket h/w state */ diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/kernel/fork.c linux-2.6.0-test1-ac1/kernel/fork.c --- linux-2.6.0-test1/kernel/fork.c 2003-07-14 14:11:57.000000000 +0100 +++ linux-2.6.0-test1-ac1/kernel/fork.c 2003-07-14 16:32:02.000000000 +0100 @@ -692,6 +692,33 @@ goto out; } +/* + * Helper to unshare the files of the current task. + * We don't want to expose copy_files internals to + * the exec layer of the kernel. + */ + +int unshare_files(void) +{ + struct files_struct *files = current->files; + int rc; + + if(!files) + BUG(); + + /* This can race but the race causes us to copy when we don't + need to and drop the copy */ + if(atomic_read(&files->count) == 1) + { + atomic_inc(&files->count); + return 0; + } + rc = copy_files(0, current); + if(rc) + current->files = files; + return rc; +} + static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk) { struct sighand_struct *sig; @@ -792,7 +819,8 @@ retval = -EAGAIN; if (atomic_read(&p->user->processes) >= p->rlim[RLIMIT_NPROC].rlim_cur) { - if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE)) + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) + && p->user != &root_user) goto bad_fork_free; } diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/kernel/ksyms.c linux-2.6.0-test1-ac1/kernel/ksyms.c --- linux-2.6.0-test1/kernel/ksyms.c 2003-07-14 14:11:57.000000000 +0100 +++ linux-2.6.0-test1-ac1/kernel/ksyms.c 2003-07-14 16:38:02.000000000 +0100 @@ -333,6 +333,7 @@ EXPORT_SYMBOL(sget); EXPORT_SYMBOL(set_anon_super); EXPORT_SYMBOL(do_select); +EXPORT_SYMBOL(unshare_files); /* for stackable file systems (lofs, wrapfs, cryptfs, etc.) */ EXPORT_SYMBOL(default_llseek); diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/kernel/sys.c linux-2.6.0-test1-ac1/kernel/sys.c --- linux-2.6.0-test1/kernel/sys.c 2003-07-14 14:11:57.000000000 +0100 +++ linux-2.6.0-test1-ac1/kernel/sys.c 2003-07-14 14:56:29.000000000 +0100 @@ -601,6 +601,14 @@ new_user = alloc_uid(new_ruid); if (!new_user) return -EAGAIN; + + if (atomic_read(&new_user->processes) >= current->rlim[RLIMIT_NPROC].rlim_cur + && new_user != &root_user + ) { + free_uid(new_user); + return -EAGAIN; + } + switch_uid(new_user); if(dumpclear) @@ -1159,6 +1167,7 @@ asmlinkage long sys_sethostname(char __user *name, int len) { int errno; + char tmp[__NEW_UTS_LEN]; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -1166,7 +1175,8 @@ return -EINVAL; down_write(&uts_sem); errno = -EFAULT; - if (!copy_from_user(system_utsname.nodename, name, len)) { + if (!copy_from_user(tmp, name, len)) { + memcpy(system_utsname.nodename, tmp, len); system_utsname.nodename[len] = 0; errno = 0; } @@ -1198,6 +1208,7 @@ asmlinkage long sys_setdomainname(char __user *name, int len) { int errno; + char tmp[__NEW_UTS_LEN]; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -1206,9 +1217,10 @@ down_write(&uts_sem); errno = -EFAULT; - if (!copy_from_user(system_utsname.domainname, name, len)) { - errno = 0; + if (!copy_from_user(tmp, name, len)) { + memcpy(system_utsname.domainname, tmp, len); system_utsname.domainname[len] = 0; + errno = 0; } up_write(&uts_sem); return errno; Binary files linux-2.6.0-test1/lib/gen_crc32table and linux-2.6.0-test1-ac1/lib/gen_crc32table differ diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/Makefile linux-2.6.0-test1-ac1/Makefile --- linux-2.6.0-test1/Makefile 2003-07-14 14:11:55.000000000 +0100 +++ linux-2.6.0-test1-ac1/Makefile 2003-07-14 14:55:04.000000000 +0100 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 0 -EXTRAVERSION = -test1 +EXTRAVERSION = -test1-ac1 # *DOCUMENTATION* # To see a list of typical targets execute "make help" @@ -781,7 +781,8 @@ tar -cvz $(RCS_TAR_IGNORE) -f $(KERNELPATH).tar.gz $(KERNELPATH)/. ; \ rm $(KERNELPATH) ; \ cd $(TOPDIR) ; \ - $(CONFIG_SHELL) $(srctree)/scripts/mkversion > .version ; \ + $(CONFIG_SHELL) $(srctree)/scripts/mkversion > .tmp_version ; \ + mv -f .tmp_version .version; \ $(RPM) -ta $(TOPDIR)/../$(KERNELPATH).tar.gz ; \ rm $(TOPDIR)/../$(KERNELPATH).tar.gz diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/net/ipv4/tcp_input.c linux-2.6.0-test1-ac1/net/ipv4/tcp_input.c --- linux-2.6.0-test1/net/ipv4/tcp_input.c 2003-07-14 14:11:57.000000000 +0100 +++ linux-2.6.0-test1-ac1/net/ipv4/tcp_input.c 2003-07-14 14:56:05.000000000 +0100 @@ -2373,7 +2373,7 @@ tcp_schedule_ack(tp); sk->sk_shutdown |= RCV_SHUTDOWN; - sock_reset_flag(sk, SOCK_DONE); + sock_set_flag(sk, SOCK_DONE); switch (sk->sk_state) { case TCP_SYN_RECV: diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/ac97_plugin_ad1980.c linux-2.6.0-test1-ac1/sound/oss/ac97_plugin_ad1980.c --- linux-2.6.0-test1/sound/oss/ac97_plugin_ad1980.c 2003-07-14 14:11:57.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/ac97_plugin_ad1980.c 2003-07-14 14:39:26.000000000 +0100 @@ -17,7 +17,7 @@ the provisions above, a recipient may use your version of this file under either the OSL or the GPL. - Authors: Arjan van de Ven + Authors: Alan Cox This is an example codec plugin. This one switches the connections around to match the setups some vendors use with audio switched to diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/ac97_plugin_wm97xx.c linux-2.6.0-test1-ac1/sound/oss/ac97_plugin_wm97xx.c --- linux-2.6.0-test1/sound/oss/ac97_plugin_wm97xx.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/ac97_plugin_wm97xx.c 2003-07-11 18:54:22.000000000 +0100 @@ -0,0 +1,1324 @@ +/* + * ac97_plugin_wm97xx.c -- Touch screen driver for Wolfson WM9705 and WM9712 + * AC97 Codecs. + * + * Copyright 2003 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.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. + * + * Notes: + * + * Features: + * - supports WM9705, WM9712 + * - polling mode + * - coordinate polling + * - adjustable rpu/dpp settings + * - adjustable pressure current + * - adjustable sample settle delay + * - 4 and 5 wire touchscreens (5 wire is WM9712 only) + * - pen down detection + * - battery monitor + * - sample AUX adc's + * - power management + * + * TODO: + * - continuous mode + * - adjustable sample rate + * - AUX adc in coordinate / continous modes + * - Official device identifier or misc device ? + * + * Revision history + * 7th May 2003 Initial version. + * 6th June 2003 Added non module support and AC97 registration. + * 18th June 2003 Added AUX adc sampling. + * 23rd June 2003 Did some minimal reformatting, fixed a couple of + * locking bugs and noted a race to fix. + * 24th June 2003 Added power management and fixed race condition. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* get_user,copy_to_user */ +#include + +#include "wm97xx.h" /* WM97xx registers and bits */ + +#define TS_NAME "wm97xx" +#define TS_MAJOR 11 +#define WM_TS_VERSION "0.4" + +/* + * Debug + */ + +#define PFX TS_NAME +#define WM97XX_TS_DEBUG 1 + +#ifdef WM97XX_TS_DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif +#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg) + +/* + * Module parameters + */ + + +/* + * Set the codec sample mode. + * + * The WM9712 can sample touchscreen data in 3 different operating + * modes. i.e. polling, coordinate and continous. + * + * Polling:- The driver polls the codec and issues 3 seperate commands + * over the AC97 link to read X,Y and pressure. + * + * Coordinate: - The driver polls the codec and only issues 1 command over + * the AC97 link to read X,Y and pressure. This mode has + * strict timing requirements and may drop samples if + * interrupted. However, it is less demanding on the AC97 + * link. Note: this mode requires a larger delay than polling + * mode. + * + * Continuous:- The codec automatically samples X,Y and pressure and then + * sends the data over the AC97 link in slots. This is the + * same method used by the codec when recording audio. + * + * Set mode = 0 for polling, 1 for coordinate and 2 for continuous. + * + */ +MODULE_PARM(mode,"i"); +MODULE_PARM_DESC(mode, "Set WM97XX operation mode"); +static int mode = 0; + +/* + * WM9712 - Set internal pull up for pen detect. + * + * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) + * i.e. pull up resistance = 64k Ohms / rpu. + * + * Adjust this value if you are having problems with pen detect not + * detecting any down events. + */ +MODULE_PARM(rpu,"i"); +MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); +static int rpu = 0; + +/* + * WM9705 - Pen detect comparator threshold. + * + * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold + * i.e. 1 = Vmid/15 threshold + * 15 = Vmid/1 threshold + * + * Adjust this value if you are having problems with pen detect not + * detecting any down events. + */ +MODULE_PARM(pdd,"i"); +MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold"); +static int pdd = 0; + +/* + * Set current used for pressure measurement. + * + * Set pil = 2 to use 400uA + * pil = 1 to use 200uA and + * pil = 0 to disable pressure measurement. + * + * This is used to increase the range of values returned by the adc + * when measureing touchpanel pressure. + */ +MODULE_PARM(pil,"i"); +MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); +static int pil = 0; + +/* + * WM9712 - Set five_wire = 1 to use a 5 wire touchscreen. + * + * NOTE: Five wire mode does not allow for readback of pressure. + */ +MODULE_PARM(five_wire,"i"); +MODULE_PARM_DESC(five_wire, "Set 5 wire touchscreen."); +static int five_wire = 0; + +/* + * Set adc sample delay. + * + * For accurate touchpanel measurements, some settling time may be + * required between the switch matrix applying a voltage across the + * touchpanel plate and the ADC sampling the signal. + * + * This delay can be set by setting delay = n, where n is the array + * position of the delay in the array delay_table below. + * Long delays > 1ms are supported for completeness, but are not + * recommended. + */ +MODULE_PARM(delay,"i"); +MODULE_PARM_DESC(delay, "Set adc sample delay."); +static int delay = 4; + + +/* +++++++++++++ Lifted from include/linux/h3600_ts.h ++++++++++++++*/ +typedef struct { + unsigned short pressure; // touch pressure + unsigned short x; // calibrated X + unsigned short y; // calibrated Y + unsigned short millisecs; // timestamp of this event +} TS_EVENT; + +typedef struct { + int xscale; + int xtrans; + int yscale; + int ytrans; + int xyswap; +} TS_CAL; + +/* Use 'f' as magic number */ +#define IOC_MAGIC 'f' + +#define TS_GET_RATE _IO(IOC_MAGIC, 8) +#define TS_SET_RATE _IO(IOC_MAGIC, 9) +#define TS_GET_CAL _IOR(IOC_MAGIC, 10, TS_CAL) +#define TS_SET_CAL _IOW(IOC_MAGIC, 11, TS_CAL) + +/* +++++++++++++ Done lifted from include/linux/h3600_ts.h +++++++++*/ + +#define TS_GET_COMP1 _IOR(IOC_MAGIC, 12, short) +#define TS_GET_COMP2 _IOR(IOC_MAGIC, 13, short) +#define TS_GET_BMON _IOR(IOC_MAGIC, 14, short) +#define TS_GET_WIPER _IOR(IOC_MAGIC, 15, short) + +#define EVENT_BUFSIZE 128 + +typedef struct { + TS_CAL cal; /* Calibration values */ + TS_EVENT event_buf[EVENT_BUFSIZE];/* The event queue */ + int nextIn, nextOut; + int event_count; + int is_wm9712:1; /* are we a WM912 or a WM9705 */ + int is_registered:1; /* Is the driver AC97 registered */ + int line_pgal:5; + int line_pgar:5; + int phone_pga:5; + int mic_pgal:5; + int mic_pgar:5; + int overruns; /* event buffer overruns */ + int adc_errs; /* sample read back errors */ + struct fasync_struct *fasync; /* asynch notification */ + struct timer_list acq_timer; /* Timer for triggering acquisitions */ + wait_queue_head_t wait; /* read wait queue */ + spinlock_t lock; + struct ac97_codec *codec; + struct proc_dir_entry *wm97xx_ts_ps; + struct pm_dev * pm; +} wm97xx_ts_t; + +static inline void poll_delay (void); +static int __init wm97xx_ts_init_module(void); +static int wm97xx_poll_read_adc (wm97xx_ts_t* ts, u16 adcsel, u16* sample); +static int wm97xx_coord_read_adc (wm97xx_ts_t* ts, u16* x, u16* y, + u16* pressure); +static inline int pendown (wm97xx_ts_t *ts); +static void wm97xx_acq_timer(unsigned long data); +static int wm97xx_fasync(int fd, struct file *filp, int mode); +static int wm97xx_ioctl(struct inode * inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static unsigned int wm97xx_poll(struct file * filp, poll_table * wait); +static ssize_t wm97xx_read(struct file * filp, char * buf, size_t count, + loff_t * l); +static int wm97xx_open(struct inode * inode, struct file * filp); +static int wm97xx_release(struct inode * inode, struct file * filp); +static void init_wm97xx_phy(void); +static int adc_get (wm97xx_ts_t *ts, unsigned short *value, int id); +static int wm97xx_probe(struct ac97_codec *codec, struct ac97_driver *driver); +static void wm97xx_remove(struct ac97_codec *codec, struct ac97_driver *driver); +static void wm97xx_ts_cleanup_module(void); +static int wm97xx_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data); +static void wm97xx_suspend(void); +static void wm97xx_resume(void); +static void wm9712_pga_save(wm97xx_ts_t* ts); +static void wm9712_pga_restore(wm97xx_ts_t* ts); + +/* AC97 registration info */ +static struct ac97_driver wm9705_driver = { + codec_id: 0x574D4C05, + codec_mask: 0xFFFFFFFF, + name: "Wolfson WM9705 Touchscreen/BMON", + probe: wm97xx_probe, + remove: __devexit_p(wm97xx_remove), +}; + +static struct ac97_driver wm9712_driver = { + codec_id: 0x574D4C12, + codec_mask: 0xFFFFFFFF, + name: "Wolfson WM9712 Touchscreen/BMON", + probe: wm97xx_probe, + remove: __devexit_p(wm97xx_remove), +}; + +/* we only support a single touchscreen */ +static wm97xx_ts_t wm97xx_ts; + +/* + * ADC sample delay times in uS + */ +static const int delay_table[16] = { + 21, // 1 AC97 Link frames + 42, // 2 + 84, // 4 + 167, // 8 + 333, // 16 + 667, // 32 + 1000, // 48 + 1333, // 64 + 2000, // 96 + 2667, // 128 + 3333, // 160 + 4000, // 192 + 4667, // 224 + 5333, // 256 + 6000, // 288 + 0 // No delay, switch matrix always on +}; + +/* + * Delay after issuing a POLL command. + * + * The delay is 3 AC97 link frames + the touchpanel settling delay + */ + +static inline void poll_delay(void) +{ + int pdelay = 3 * AC97_LINK_FRAME + delay_table[delay]; + udelay (pdelay); +} + + +/* + * sample the auxillary ADC's + */ + +static int adc_get(wm97xx_ts_t* ts, unsigned short * value, int id) +{ + short adcsel = 0; + + /* first find out our adcsel flag */ + if (ts->is_wm9712) { + switch (id) { + case TS_COMP1: + adcsel = WM9712_ADCSEL_COMP1; + break; + case TS_COMP2: + adcsel = WM9712_ADCSEL_COMP2; + break; + case TS_BMON: + adcsel = WM9712_ADCSEL_BMON; + break; + case TS_WIPER: + adcsel = WM9712_ADCSEL_WIPER; + break; + } + } else { + switch (id) { + case TS_COMP1: + adcsel = WM9705_ADCSEL_PCBEEP; + break; + case TS_COMP2: + adcsel = WM9705_ADCSEL_PHONE; + break; + case TS_BMON: + adcsel = WM9705_ADCSEL_BMON; + break; + case TS_WIPER: + adcsel = WM9705_ADCSEL_AUX; + break; + } + } + + /* now sample the adc */ + if (mode == 1) { + /* coordinate mode - not currently available (TODO) */ + return 0; + } + else + { + /* polling mode */ + if (!wm97xx_poll_read_adc(ts, adcsel, value)) + return 0; + } + + return 1; +} + + +/* + * Read a sample from the adc in polling mode. + */ +static int wm97xx_poll_read_adc (wm97xx_ts_t* ts, u16 adcsel, u16* sample) +{ + u16 dig1; + int timeout = 5 * delay; + + /* set up digitiser */ + dig1 = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1); + dig1&=0x0fff; + ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER1, dig1 | adcsel | + WM97XX_POLL); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(); + + /* wait for POLL to go low */ + while ((ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + if (timeout > 0) + *sample = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD); + else { + ts->adc_errs++; + err ("adc sample timeout"); + return 0; + } + + /* check we have correct sample */ + if ((*sample & 0x7000) != adcsel ) { + err ("adc wrong sample, read %x got %x", adcsel, *sample & 0x7000); + return 0; + } + return 1; +} + +/* + * Read a sample from the adc in coordinate mode. + */ +static int wm97xx_coord_read_adc(wm97xx_ts_t* ts, u16* x, u16* y, u16* pressure) +{ + u16 dig1; + int timeout = 5 * delay; + + /* set up digitiser */ + dig1 = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1); + dig1&=0x0fff; + ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER1, dig1 | WM97XX_ADCSEL_PRES | + WM97XX_POLL); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(); + + /* read X then wait for 1 AC97 link frame + settling delay */ + *x = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD); + udelay (AC97_LINK_FRAME + delay_table[delay]); + + /* read Y */ + *y = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD); + + /* wait for POLL to go low and then read pressure */ + while ((ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)&& timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + if (timeout > 0) + *pressure = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD); + else { + ts->adc_errs++; + err ("adc sample timeout"); + return 0; + } + + /* check we have correct samples */ + if (((*x & 0x7000) == 0x1000) && ((*y & 0x7000) == 0x2000) && + ((*pressure & 0x7000) == 0x3000)) { + return 1; + } else { + ts->adc_errs++; + err ("adc got wrong samples, got x 0x%x y 0x%x pressure 0x%x", *x, *y, *pressure); + return 0; + } +} + +/* + * Is the pen down ? + */ +static inline int pendown (wm97xx_ts_t *ts) +{ + return ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN; +} + +/* + * X,Y coordinates and pressure aquisition function. + * This function is run by a kernel timer and it's frequency between + * calls is the touchscreen polling rate; + */ + +static void wm97xx_acq_timer(unsigned long data) +{ + wm97xx_ts_t* ts = (wm97xx_ts_t*)data; + unsigned long flags; + long x,y; + TS_EVENT event; + + spin_lock_irqsave(&ts->lock, flags); + + /* are we still registered ? */ + if (!ts->is_registered) { + spin_unlock_irqrestore(&ts->lock, flags); + return; /* we better stop then */ + } + + /* read coordinates if pen is down */ + if (!pendown(ts)) + goto acq_exit; + + if (mode == 1) { + /* coordinate mode */ + if (!wm97xx_coord_read_adc(ts, (u16*)&x, (u16*)&y, &event.pressure)) + goto acq_exit; + } else + { + /* polling mode */ + if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_X, (u16*)&x)) + goto acq_exit; + if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_Y, (u16*)&y)) + goto acq_exit; + + /* only read pressure if we have to */ + if (!five_wire && pil) { + if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_PRES, &event.pressure)) + goto acq_exit; + } + else + event.pressure = 0; + } + /* timestamp this new event. */ + event.millisecs = jiffies; + + /* calibrate and remove unwanted bits from samples */ + event.pressure &= 0x0fff; + + x &= 0x00000fff; + x = ((ts->cal.xscale * x) >> 8) + ts->cal.xtrans; + event.x = (u16)x; + + y &= 0x00000fff; + y = ((ts->cal.yscale * y) >> 8) + ts->cal.ytrans; + event.y = (u16)y; + + /* add this event to the event queue */ + ts->event_buf[ts->nextIn++] = event; + if (ts->nextIn == EVENT_BUFSIZE) + ts->nextIn = 0; + if (ts->event_count < EVENT_BUFSIZE) { + ts->event_count++; + } else { + /* throw out the oldest event */ + if (++ts->nextOut == EVENT_BUFSIZE) { + ts->nextOut = 0; + ts->overruns++; + } + } + + /* async notify */ + if (ts->fasync) + kill_fasync(&ts->fasync, SIGIO, POLL_IN); + /* wake up any read call */ + if (waitqueue_active(&ts->wait)) + wake_up_interruptible(&ts->wait); + + /* schedule next acquire */ +acq_exit: + ts->acq_timer.expires = jiffies + HZ / 100; + add_timer(&ts->acq_timer); + + spin_unlock_irqrestore(&ts->lock, flags); +} + + +/* +++++++++++++ File operations ++++++++++++++*/ + +static int wm97xx_fasync(int fd, struct file *filp, int mode) +{ + wm97xx_ts_t* ts = (wm97xx_ts_t*)filp->private_data; + return fasync_helper(fd, filp, mode, &ts->fasync); +} + +static int wm97xx_ioctl(struct inode * inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + unsigned short adc_value; + wm97xx_ts_t* ts = (wm97xx_ts_t*)filp->private_data; + + switch(cmd) { + case TS_GET_RATE: /* TODO: what is this? */ + break; + case TS_SET_RATE: /* TODO: what is this? */ + break; + case TS_GET_CAL: + if(copy_to_user((char *)arg, (char *)&ts->cal, sizeof(TS_CAL))) + return -EFAULT; + break; + case TS_SET_CAL: + if(copy_from_user((char *)&ts->cal, (char *)arg, sizeof(TS_CAL))) + return -EFAULT; + break; + case TS_GET_COMP1: + if (adc_get(ts, &adc_value, TS_COMP1)) + { + if(copy_to_user((char *)arg, (char *)&adc_value, sizeof(adc_value))) + return -EFAULT; + } + else + return -EIO; + break; + case TS_GET_COMP2: + if (adc_get(ts, &adc_value, TS_COMP2)) + { + if(copy_to_user((char *)arg, (char *)&adc_value, sizeof(adc_value))) + return -EFAULT; + } + else + return -EIO; + break; + case TS_GET_BMON: + if (adc_get(ts, &adc_value, TS_BMON)) + { + if(copy_to_user((char *)arg, (char *)&adc_value, sizeof(adc_value))) + return -EFAULT; + } + else + return -EIO; + break; + case TS_GET_WIPER: + if (adc_get(ts, &adc_value, TS_WIPER)) + { + if(copy_to_user((char *)arg, (char *)&adc_value, sizeof(adc_value))) + return -EFAULT; + } + else + return -EIO; + break; + default: + return -EINVAL; + } + + return 0; +} + +static unsigned int wm97xx_poll(struct file * filp, poll_table * wait) +{ + wm97xx_ts_t *ts = (wm97xx_ts_t *)filp->private_data; + poll_wait(filp, &ts->wait, wait); + if (ts->event_count) + return POLLIN | POLLRDNORM; + return 0; +} + +static ssize_t wm97xx_read(struct file *filp, char *buf, size_t count, loff_t *l) +{ + wm97xx_ts_t* ts = (wm97xx_ts_t*)filp->private_data; + unsigned long flags; + TS_EVENT event; + int i; + + /* are we still registered with AC97 layer ? */ + spin_lock_irqsave(&ts->lock, flags); + if (!ts->is_registered) { + spin_unlock_irqrestore(&ts->lock, flags); + return -ENXIO; + } + + if (ts->event_count == 0) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + spin_unlock_irqrestore(&ts->lock, flags); + + wait_event_interruptible(ts->wait, ts->event_count != 0); + + /* are we still registered after sleep ? */ + spin_lock_irqsave(&ts->lock, flags); + if (!ts->is_registered) { + spin_unlock_irqrestore(&ts->lock, flags); + return -ENXIO; + } + if (signal_pending(current)) + return -ERESTARTSYS; + } + + for (i = count; i >= sizeof(TS_EVENT); + i -= sizeof(TS_EVENT), buf += sizeof(TS_EVENT)) { + if (ts->event_count == 0) + break; + spin_lock_irqsave(&ts->lock, flags); + event = ts->event_buf[ts->nextOut++]; + if (ts->nextOut == EVENT_BUFSIZE) + ts->nextOut = 0; + if (ts->event_count) + ts->event_count--; + spin_unlock_irqrestore(&ts->lock, flags); + if(copy_to_user(buf, &event, sizeof(TS_EVENT))) + return i != count ? count - i : -EFAULT; + } + return count - i; +} + + +static int wm97xx_open(struct inode * inode, struct file * filp) +{ + wm97xx_ts_t* ts; + unsigned long flags; + u16 val; + + filp->private_data = ts = &wm97xx_ts; + + spin_lock_irqsave(&ts->lock, flags); + + /* are we registered with AC97 layer ? */ + if (!ts->is_registered) { + spin_unlock_irqrestore(&ts->lock, flags); + return -ENXIO; + } + + /* start digitiser */ + val = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER2); + ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER2, + val | WM97XX_PRP_DET_DIG); + + /* flush event queue */ + ts->nextIn = ts->nextOut = ts->event_count = 0; + + /* Set up timer. */ + init_timer(&ts->acq_timer); + ts->acq_timer.function = wm97xx_acq_timer; + ts->acq_timer.data = (unsigned long)ts; + ts->acq_timer.expires = jiffies + HZ / 100; + add_timer(&ts->acq_timer); + + spin_unlock_irqrestore(&ts->lock, flags); + return 0; +} + +static int wm97xx_release(struct inode * inode, struct file * filp) +{ + wm97xx_ts_t* ts = (wm97xx_ts_t*)filp->private_data; + unsigned long flags; + u16 val; + + wm97xx_fasync(-1, filp, 0); + del_timer_sync(&ts->acq_timer); + + spin_lock_irqsave(&ts->lock, flags); + + /* stop digitiser */ + val = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER2); + ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER2, + val & ~WM97XX_PRP_DET_DIG); + + spin_unlock_irqrestore(&ts->lock, flags); + return 0; +} + +static struct file_operations ts_fops = { + owner: THIS_MODULE, + read: wm97xx_read, + poll: wm97xx_poll, + ioctl: wm97xx_ioctl, + fasync: wm97xx_fasync, + open: wm97xx_open, + release: wm97xx_release, +}; + +/* +++++++++++++ End File operations ++++++++++++++*/ + +#ifdef CONFIG_PROC_FS +static int wm97xx_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0, prpu; + u16 dig1, dig2, digrd, adcsel, adcsrc, slt, prp, rev; + unsigned long flags; + char srev = ' '; + + wm97xx_ts_t* ts; + + if ((ts = data) == NULL) + return -ENODEV; + + spin_lock_irqsave(&ts->lock, flags); + if (!ts->is_registered) { + spin_unlock_irqrestore(&ts->lock, flags); + len += sprintf (page+len, "No device registered\n"); + return len; + } + + dig1 = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1); + dig2 = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER2); + digrd = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD); + rev = ts->codec->codec_read(ts->codec, AC97_WM9712_REV) & 0x000c; + + spin_unlock_irqrestore(&ts->lock, flags); + + adcsel = dig1 & 0x7000; + adcsrc = digrd & 0x7000; + slt = (dig1 & 0x7) + 5; + prp = dig2 & 0xc000; + prpu = dig2 & 0x003f; + + /* driver version */ + len += sprintf (page+len, "Wolfson WM97xx Version %s\n", WM_TS_VERSION); + + /* what we are using */ + len += sprintf (page+len, "Using %s", ts->is_wm9712 ? "WM9712" : "WM9705"); + if (ts->is_wm9712) { + switch (rev) { + case 0x0: + srev = 'A'; + break; + case 0x1: + srev = 'B'; + break; + case 0x2: + srev = 'D'; + break; + case 0x3: + srev = 'E'; + break; + } + len += sprintf (page+len, " silicon rev %c\n",srev); + } else + len += sprintf (page+len, "\n"); + + /* WM97xx settings */ + len += sprintf (page+len, "Settings :\n%s%s%s%s", + dig1 & WM97XX_POLL ? " -sampling adc data(poll)\n" : "", + adcsel == WM97XX_ADCSEL_X ? " -adc set to X coordinate\n" : "", + adcsel == WM97XX_ADCSEL_Y ? " -adc set to Y coordinate\n" : "", + adcsel == WM97XX_ADCSEL_PRES ? " -adc set to pressure\n" : ""); + if (ts->is_wm9712) { + len += sprintf (page+len, "%s%s%s%s", + adcsel == WM9712_ADCSEL_COMP1 ? " -adc set to COMP1/AUX1\n" : "", + adcsel == WM9712_ADCSEL_COMP2 ? " -adc set to COMP2/AUX2\n" : "", + adcsel == WM9712_ADCSEL_BMON ? " -adc set to BMON\n" : "", + adcsel == WM9712_ADCSEL_WIPER ? " -adc set to WIPER\n" : ""); + } else { + len += sprintf (page+len, "%s%s%s%s", + adcsel == WM9705_ADCSEL_PCBEEP ? " -adc set to PCBEEP\n" : "", + adcsel == WM9705_ADCSEL_PHONE ? " -adc set to PHONE\n" : "", + adcsel == WM9705_ADCSEL_BMON ? " -adc set to BMON\n" : "", + adcsel == WM9705_ADCSEL_AUX ? " -adc set to AUX\n" : ""); + } + + len += sprintf (page+len, "%s%s%s%s%s%s", + dig1 & WM97XX_COO ? " -coordinate sampling\n" : " -individual sampling\n", + dig1 & WM97XX_CTC ? " -continuous mode\n" : " -polling mode\n", + prp == WM97XX_PRP_DET ? " -pen detect enabled, no wake up\n" : "", + prp == WM97XX_PRP_DETW ? " -pen detect enabled, wake up\n" : "", + prp == WM97XX_PRP_DET_DIG ? " -pen digitiser and pen detect enabled\n" : "", + dig1 & WM97XX_SLEN ? " -read back using slot " : " -read back using AC97\n"); + + if ((dig1 & WM97XX_SLEN) && slt !=12) + len += sprintf(page+len, "%d\n", slt); + len += sprintf (page+len, " -adc sample delay %d uSecs\n", delay_table[(dig1 & 0x00f0) >> 4]); + + if (ts->is_wm9712) { + if (prpu) + len += sprintf (page+len, " -rpu %d Ohms\n", 64000/ prpu); + len += sprintf (page+len, " -pressure current %s uA\n", dig2 & WM9712_PIL ? "400" : "200"); + len += sprintf (page+len, " -using %s wire touchscreen mode", dig2 & WM9712_45W ? "5" : "4"); + } else { + len += sprintf (page+len, " -pressure current %s uA\n", dig2 & WM9705_PIL ? "400" : "200"); + len += sprintf (page+len, " -%s impedance for PHONE and PCBEEP\n", dig2 & WM9705_PHIZ ? "high" : "low"); + } + + /* WM97xx digitiser read */ + len += sprintf(page+len, "\nADC data:\n%s%d\n%s%s\n", + " -adc value (decimal) : ", digrd & 0x0fff, + " -pen ", digrd & 0x8000 ? "Down" : "Up"); + if (ts->is_wm9712) { + len += sprintf (page+len, "%s%s%s%s", + adcsrc == WM9712_ADCSEL_COMP1 ? " -adc value is COMP1/AUX1\n" : "", + adcsrc == WM9712_ADCSEL_COMP2 ? " -adc value is COMP2/AUX2\n" : "", + adcsrc == WM9712_ADCSEL_BMON ? " -adc value is BMON\n" : "", + adcsrc == WM9712_ADCSEL_WIPER ? " -adc value is WIPER\n" : ""); + } else { + len += sprintf (page+len, "%s%s%s%s", + adcsrc == WM9705_ADCSEL_PCBEEP ? " -adc value is PCBEEP\n" : "", + adcsrc == WM9705_ADCSEL_PHONE ? " -adc value is PHONE\n" : "", + adcsrc == WM9705_ADCSEL_BMON ? " -adc value is BMON\n" : "", + adcsrc == WM9705_ADCSEL_AUX ? " -adc value is AUX\n" : ""); + } + + /* register dump */ + len += sprintf(page+len, "\nRegisters:\n%s%x\n%s%x\n%s%x\n", + " -digitiser 1 (0x76) : 0x", dig1, + " -digitiser 2 (0x78) : 0x", dig2, + " -digitiser read (0x7a) : 0x", digrd); + + /* errors */ + len += sprintf(page+len, "\nErrors:\n%s%d\n%s%d\n", + " -buffer overruns ", ts->overruns, + " -coordinate errors ", ts->adc_errs); + + return len; +} +#endif + +#ifdef CONFIG_PM +/* WM97xx Power Management + * The WM9712 has extra powerdown states that are controlled in + * seperate registers from the AC97 power management. + * We will only power down into the extra WM9712 states and leave + * the AC97 power management to the sound driver. + */ +static int wm97xx_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + switch(rqst) { + case PM_SUSPEND: + wm97xx_suspend(); + break; + case PM_RESUME: + wm97xx_resume(); + break; + } + return 0; +} + +/* + * Power down the codec + */ +static void wm97xx_suspend(void) +{ + wm97xx_ts_t* ts = &wm97xx_ts; + u16 reg; + unsigned long flags; + + /* are we registered */ + spin_lock_irqsave(&ts->lock, flags); + if (!ts->is_registered) { + spin_unlock_irqrestore(&ts->lock, flags); + return; + } + + /* wm9705 does not have extra PM */ + if (!ts->is_wm9712) { + spin_unlock_irqrestore(&ts->lock, flags); + return; + } + + /* save and mute the PGA's */ + wm9712_pga_save(ts); + + reg = ts->codec->codec_read(ts->codec, AC97_PHONE_VOL); + ts->codec->codec_write(ts->codec, AC97_PHONE_VOL, reg | 0x001f); + + reg = ts->codec->codec_read(ts->codec, AC97_MIC_VOL); + ts->codec->codec_write(ts->codec, AC97_MIC_VOL, reg | 0x1f1f); + + reg = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL); + ts->codec->codec_write(ts->codec, AC97_LINEIN_VOL, reg | 0x1f1f); + + /* power down, dont disable the AC link */ + ts->codec->codec_write(ts->codec, AC97_WM9712_POWER, WM9712_PD(14) | WM9712_PD(13) | + WM9712_PD(12) | WM9712_PD(11) | WM9712_PD(10) | + WM9712_PD(9) | WM9712_PD(8) | WM9712_PD(7) | + WM9712_PD(6) | WM9712_PD(5) | WM9712_PD(4) | + WM9712_PD(3) | WM9712_PD(2) | WM9712_PD(1) | + WM9712_PD(0)); + + spin_unlock_irqrestore(&ts->lock, flags); +} + +/* + * Power up the Codec + */ +static void wm97xx_resume(void) +{ + wm97xx_ts_t* ts = &wm97xx_ts; + unsigned long flags; + + /* are we registered */ + spin_lock_irqsave(&ts->lock, flags); + if (!ts->is_registered) { + spin_unlock_irqrestore(&ts->lock, flags); + return; + } + + /* wm9705 does not have extra PM */ + if (!ts->is_wm9712) { + spin_unlock_irqrestore(&ts->lock, flags); + return; + } + + /* power up */ + ts->codec->codec_write(ts->codec, AC97_WM9712_POWER, 0x0); + + /* restore PGA state */ + wm9712_pga_restore(ts); + + spin_unlock_irqrestore(&ts->lock, flags); +} + + +/* save state of wm9712 PGA's */ +static void wm9712_pga_save(wm97xx_ts_t* ts) +{ + ts->phone_pga = ts->codec->codec_read(ts->codec, AC97_PHONE_VOL) & 0x001f; + ts->line_pgal = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL) & 0x1f00; + ts->line_pgar = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL) & 0x001f; + ts->mic_pgal = ts->codec->codec_read(ts->codec, AC97_MIC_VOL) & 0x1f00; + ts->mic_pgar = ts->codec->codec_read(ts->codec, AC97_MIC_VOL) & 0x001f; +} + +/* restore state of wm9712 PGA's */ +static void wm9712_pga_restore(wm97xx_ts_t* ts) +{ + u16 reg; + + reg = ts->codec->codec_read(ts->codec, AC97_PHONE_VOL); + ts->codec->codec_write(ts->codec, AC97_PHONE_VOL, reg | ts->phone_pga); + + reg = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL); + ts->codec->codec_write(ts->codec, AC97_LINEIN_VOL, reg | ts->line_pgar | (ts->line_pgal << 8)); + + reg = ts->codec->codec_read(ts->codec, AC97_MIC_VOL); + ts->codec->codec_write(ts->codec, AC97_MIC_VOL, reg | ts->mic_pgar | (ts->mic_pgal << 8)); +} + +#endif + +/* + * set up the physical settings of the device + */ + +static void init_wm97xx_phy(void) +{ + u16 dig1, dig2, aux, vid; + wm97xx_ts_t *ts = &wm97xx_ts; + + /* default values */ + dig1 = WM97XX_DELAY(4) | WM97XX_SLT(6); + if (ts->is_wm9712) + dig2 = WM9712_RPU(1); + else { + dig2 = 0x0; + + /* + * mute VIDEO and AUX as they share X and Y touchscreen + * inputs on the WM9705 + */ + aux = ts->codec->codec_read(ts->codec, AC97_AUX_VOL); + if (!(aux & 0x8000)) { + info("muting AUX mixer as it shares X touchscreen coordinate"); + ts->codec->codec_write(ts->codec, AC97_AUX_VOL, 0x8000 | aux); + } + + vid = ts->codec->codec_read(ts->codec, AC97_VIDEO_VOL); + if (!(vid & 0x8000)) { + info("muting VIDEO mixer as it shares Y touchscreen coordinate"); + ts->codec->codec_write(ts->codec, AC97_VIDEO_VOL, 0x8000 | vid); + } + } + + /* WM9712 rpu */ + if (ts->is_wm9712 && rpu) { + dig2 &= 0xffc0; + dig2 |= WM9712_RPU(rpu); + info("setting pen detect pull-up to %d Ohms",64000 / rpu); + } + + /* touchpanel pressure */ + if (pil == 2) { + if (ts->is_wm9712) + dig2 |= WM9712_PIL; + else + dig2 |= WM9705_PIL; + info("setting pressure measurement current to 400uA."); + } else if (pil) + info ("setting pressure measurement current to 200uA."); + + /* WM9712 five wire */ + if (ts->is_wm9712 && five_wire) { + dig2 |= WM9712_45W; + info("setting 5-wire touchscreen mode."); + } + + /* sample settling delay */ + if (delay!=4) { + if (delay < 0 || delay > 15) { + info ("supplied delay out of range."); + delay = 4; + } + dig1 &= 0xff0f; + dig1 |= WM97XX_DELAY(delay); + info("setting adc sample delay to %d u Secs.", delay_table[delay]); + } + + /* coordinate mode */ + if (mode == 1) { + dig1 |= WM97XX_COO; + info("using coordinate mode"); + } + + /* WM9705 pdd */ + if (pdd && !ts->is_wm9712) { + dig2 |= (pdd & 0x000f); + info("setting pdd to Vmid/%d", 1 - (pdd & 0x000f)); + } + + ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER1, dig1); + ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER2, dig2); +} + + +/* + * Called by the audio codec initialisation to register + * the touchscreen driver. + */ + +static int wm97xx_probe(struct ac97_codec *codec, struct ac97_driver *driver) +{ + unsigned long flags; + u16 id1, id2; + wm97xx_ts_t *ts = &wm97xx_ts; + + spin_lock_irqsave(&ts->lock, flags); + + /* we only support 1 touchscreen at the moment */ + if (ts->is_registered) { + spin_unlock_irqrestore(&ts->lock, flags); + return -1; + } + + /* + * We can only use a WM9705 or WM9712 that has been *first* initialised + * by the AC97 audio driver. This is because we have to use the audio + * drivers codec read() and write() functions to sample the touchscreen + * + * If an initialsed WM97xx is found then get the codec read and write + * functions. + */ + + /* test for a WM9712 or a WM9705 */ + id1 = codec->codec_read(codec, AC97_VENDOR_ID1); + id2 = codec->codec_read(codec, AC97_VENDOR_ID2); + if (id1 == WM97XX_ID1 && id2 == WM9712_ID2) { + ts->is_wm9712 = 1; + info("registered a WM9712"); + } else if (id1 == WM97XX_ID1 && id2 == WM9705_ID2) { + ts->is_wm9712 = 0; + info("registered a WM9705"); + } else { + err("could not find a WM97xx codec. Found a 0x%4x:0x%4x instead", + id1, id2); + spin_unlock_irqrestore(&ts->lock, flags); + return -1; + } + + /* set up AC97 codec interface */ + ts->codec = codec; + codec->driver_private = (void*)&ts; + codec->codec_unregister = 0; + + /* set up physical characteristics */ + init_wm97xx_phy(); + + ts->is_registered = 1; + spin_unlock_irqrestore(&ts->lock, flags); + return 0; +} + +/* this is called by the audio driver when ac97_codec is unloaded */ + +static void wm97xx_remove(struct ac97_codec *codec, struct ac97_driver *driver) +{ + unsigned long flags; + u16 dig1, dig2; + wm97xx_ts_t *ts = codec->driver_private; + + spin_lock_irqsave(&ts->lock, flags); + + /* check that are registered */ + if (!ts->is_registered) { + err("double unregister"); + spin_unlock_irqrestore(&ts->lock, flags); + return; + } + + ts->is_registered = 0; + wake_up_interruptible(&ts->wait); /* So we see its gone */ + + /* restore default digitiser values */ + dig1 = WM97XX_DELAY(4) | WM97XX_SLT(6); + if (ts->is_wm9712) + dig2 = WM9712_RPU(1); + else + dig2 = 0x0; + + codec->codec_write(codec, AC97_WM97XX_DIGITISER1, dig1); + codec->codec_write(codec, AC97_WM97XX_DIGITISER2, dig2); + ts->codec = NULL; + + spin_unlock_irqrestore(&ts->lock, flags); +} + +static int __init wm97xx_ts_init_module(void) +{ + wm97xx_ts_t* ts = &wm97xx_ts; + int ret; + char proc_str[64]; + + info("Wolfson WM9705/WM9712 Touchscreen Controller"); + info("Version %s liam.girdwood@wolfsonmicro.com", WM_TS_VERSION); + + memset(ts, 0, sizeof(wm97xx_ts_t)); + + /* register our character device */ + if ((ret = register_chrdev(TS_MAJOR, TS_NAME, &ts_fops)) < 0) { + err("can't get major number"); + return ret; + } + + init_waitqueue_head(&ts->wait); + spin_lock_init(&ts->lock); + + // initial calibration values + ts->cal.xscale = 256; + ts->cal.xtrans = 0; + ts->cal.yscale = 256; + ts->cal.ytrans = 0; + + /* reset error counters */ + ts->overruns = 0; + ts->adc_errs = 0; + + /* register with the AC97 layer */ + ac97_register_driver(&wm9705_driver); + ac97_register_driver(&wm9712_driver); + +#ifdef CONFIG_PROC_FS + /* register proc interface */ + sprintf(proc_str, "driver/%s", TS_NAME); + if ((ts->wm97xx_ts_ps = create_proc_read_entry (proc_str, 0, NULL, + wm97xx_read_proc, ts)) == 0) + err("could not register proc interface /proc/%s", proc_str); +#endif +#ifdef CONFIG_PM + if ((ts->pm = pm_register(PM_UNKNOWN_DEV, PM_SYS_UNKNOWN, wm97xx_pm_event)) == 0) + err("could not register with power management"); +#endif + return 0; +} + +static void wm97xx_ts_cleanup_module(void) +{ + wm97xx_ts_t* ts = &wm97xx_ts; + +#ifdef CONFIG_PM + pm_unregister (ts->pm); +#endif + ac97_unregister_driver(&wm9705_driver); + ac97_unregister_driver(&wm9712_driver); + unregister_chrdev(TS_MAJOR, TS_NAME); +} + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_DESCRIPTION("WM9705/WM9712 Touch Screen / BMON Driver"); +MODULE_LICENSE("GPL"); + +module_init(wm97xx_ts_init_module); +module_exit(wm97xx_ts_cleanup_module); + +#ifndef MODULE + +static int __init wm97xx_ts_setup(char *options) +{ + char *this_opt = options; + + if (!options || !*options) + return 0; + + /* parse the options and check for out of range values */ + for(this_opt=strtok(options, ","); + this_opt; this_opt=strtok(NULL, ",")) { + if (!strncmp(this_opt, "pil:", 4)) { + this_opt+=4; + pil = simple_strtol(this_opt, NULL, 0); + if (pil < 0 || pil > 2) + pil = 0; + continue; + } + if (!strncmp(this_opt, "rpu:", 4)) { + this_opt+=4; + rpu = simple_strtol(this_opt, NULL, 0); + if (rpu < 0 || rpu > 31) + rpu = 0; + continue; + } + if (!strncmp(this_opt, "pdd:", 4)) { + this_opt+=4; + pdd = simple_strtol(this_opt, NULL, 0); + if (pdd < 0 || pdd > 15) + pdd = 0; + continue; + } + if (!strncmp(this_opt, "delay:", 6)) { + this_opt+=6; + delay = simple_strtol(this_opt, NULL, 0); + if (delay < 0 || delay > 15) + delay = 4; + continue; + } + if (!strncmp(this_opt, "five_wire:", 10)) { + this_opt+=10; + five_wire = simple_strtol(this_opt, NULL, 0); + if (five_wire < 0 || five_wire > 1) + five_wire = 0; + continue; + } + if (!strncmp(this_opt, "mode:", 5)) { + this_opt+=5; + mode = simple_strtol(this_opt, NULL, 0); + if (mode < 0 || mode > 2) + mode = 0; + continue; + } + } + return 1; +} + +__setup("wm97xx_ts=", wm97xx_ts_setup); + +#endif /* MODULE */ diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/ad1889.c linux-2.6.0-test1-ac1/sound/oss/ad1889.c --- linux-2.6.0-test1/sound/oss/ad1889.c 2003-07-14 14:11:57.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/ad1889.c 2003-07-14 14:48:51.000000000 +0100 @@ -245,7 +245,6 @@ dmabuf->ready = 0; dmabuf->rate = 44100; } -out: return dev; err_free_dmabuf: diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/ali5455.c linux-2.6.0-test1-ac1/sound/oss/ali5455.c --- linux-2.6.0-test1/sound/oss/ali5455.c 2003-07-14 14:11:57.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/ali5455.c 2003-07-11 18:51:36.000000000 +0100 @@ -6,7 +6,7 @@ * drivers/sound/i810_audio * * The ALi 5455 is similar but not quite identical to the Intel ICH - * series of controllers. Its easier to keep the driver separated from + * series of controllers. Its easier to keep the driver seperated from * the i810 driver. * * This program is free software; you can redistribute it and/or modify @@ -117,7 +117,7 @@ #define SG_LEN 32 struct ali_channel { /* these sg guys should probably be allocated - separately as nocache. Must be 8 byte aligned */ + seperately as nocache. Must be 8 byte aligned */ struct sg_item sg[SG_LEN]; /* 32*8 */ u32 offset; /* 4 */ u32 port; /* 4 */ @@ -126,7 +126,7 @@ }; /* - * we have 3 separate dma engines. pcm in, pcm out, and mic. + * 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. @@ -903,7 +903,7 @@ #define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) #define DMABUF_MINORDER 1 -/* allocate DMA buffer, playback , recording,spdif out buffer should be allocated separately */ +/* allocate DMA buffer, playback , recording,spdif out buffer should be allocated seperately */ static int alloc_dmabuf(struct ali_state *state) { struct dmabuf *dmabuf = &state->dmabuf; diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/btaudio.c linux-2.6.0-test1-ac1/sound/oss/btaudio.c --- linux-2.6.0-test1/sound/oss/btaudio.c 2003-07-14 14:11:57.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/btaudio.c 2003-07-14 14:27:14.000000000 +0100 @@ -328,8 +328,8 @@ if (cmd == SOUND_MIXER_INFO) { mixer_info info; memset(&info,0,sizeof(info)); - strncpy(info.id,"bt878",sizeof(info.id)); - strncpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)); + strlcpy(info.id,"bt878",sizeof(info.id)); + strlcpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)); info.modify_counter = bta->mixcount; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; @@ -338,8 +338,8 @@ if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; memset(&info,0,sizeof(info)); - strncpy(info.id,"bt878",sizeof(info.id)-1); - strncpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)); + strlcpy(info.id,"bt878",sizeof(info.id)-1); + strlcpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/cs4281/cs4281m.c linux-2.6.0-test1-ac1/sound/oss/cs4281/cs4281m.c --- linux-2.6.0-test1/sound/oss/cs4281/cs4281m.c 2003-07-10 21:15:34.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/cs4281/cs4281m.c 2003-07-11 17:52:29.000000000 +0100 @@ -1,12 +1,12 @@ /******************************************************************************* * -* "cs4281.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* "cs4281m.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). +* (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 @@ -51,6 +51,18 @@ * 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. +* 04/02/01 trw - reworked the includes, so kgdb wouldn't get confused. +* 04/17/01 trw - added ifdef CONFIG_PM for 2.4.x non-pm kernels. static pmprint. +* 04/19/01 trw - reworked all of the wrapper macros to keep native 2.4.x code +* predominate in the driver. +* 07/01/01 trw - added ability to modify the record source mask to alleviate +* problems with toshiba systems. also, check for toshiba +* system to set default up properly. +* 11/12/01 trw - removed cs4281_update_ptr() in the polling interface code. +* returning with only a few bytes available in the write buffer +* seems to cause some problems with some apps (xmms OSS plugin). +* Also, fixed bug in cs4281_update_ptr() code to wakeup when +* 1/2 buffer is empty, not when completely full. * *******************************************************************************/ @@ -101,18 +113,40 @@ #define PCI_DEVICE_ID_CRYSTAL_CS4281 0x6005 #endif +#ifndef SS_ID_TOSHIBA_1640CDT +#define SS_ID_TOSHIBA_1640CDT 0xff00 +#endif + +#ifndef PCI_VENDOR_ID_TOSHIBA +#define PCI_VENDOR_ID_TOSHIBA 0x1179 +#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 */ +#define CS4281_BA0_CPWR_DEFAULT 0x4281 -// 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). +/* 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. +* (2^N) * PAGE_SIZE = allocated buffer size +* +* also 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. +* games are even MORE responsive now, but prone to underruns. +*/ static unsigned long defaultorder = 3; MODULE_PARM(defaultorder, "i"); +/* +* use this module parm to invalidate recording sources +* as on some machines (Toshiba Satellites... again) setting to LINE +* causes an error and some of the mixers (gmix) to not load. +*/ +static unsigned long recsrc_invalid = 0; +MODULE_PARM(recsrc_invalid, "i"); // // Turn on/off debugging compilation by commenting out "#define CSDEBUG" // @@ -174,7 +208,7 @@ #define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) #define CS4281_MAJOR_VERSION 1 -#define CS4281_MINOR_VERSION 13 +#define CS4281_MINOR_VERSION 30 #ifdef __ia64__ #define CS4281_ARCH 64 //architecture key #else @@ -196,16 +230,12 @@ } \ }) -//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; + u16 ss_id, ss_vendor; /* subsystem and vendor IDs from pci space */ // we keep the cards in a linked list struct cs4281_state *next; @@ -223,6 +253,7 @@ unsigned int pBA0phys, pBA1phys; char *pBA0, *pBA1; unsigned int irq; + unsigned recsrc; // mixer registers struct { @@ -293,12 +324,12 @@ unsigned char obuf[MIDIOUTBUF]; } midi; +#ifndef NOT_CS4281_PM struct cs4281_pm pm; struct cs4281_pipeline pl[CS4281_NUMBER_OF_PIPELINES]; +#endif }; -#include "cs4281pm-24.c" - #if CSDEBUG // DEBUG ROUTINES @@ -529,7 +560,13 @@ static void delayus(struct cs4281_state *s, u32 delay) { u32 j; - if ((delay > 9999) && (s->pm.flags & CS4281_PM_IDLE)) { + if ((delay > 9999) +#ifndef NOT_CS4281_PM + && (s->pm.flags & CS4281_PM_IDLE)) +#else + ) +#endif + { j = (delay * HZ) / 1000000; /* calculate delay in jiffies */ if (j < 1) j = 1; /* minimum one jiffy. */ @@ -580,7 +617,7 @@ card->pBA0 + BA0_ACCTL); // Wait for the read to occur. - for (count = 0; count < 10; count++) { + for (count = 0; count < 100; count++) { // First, we want to wait for a short time. udelay(25); @@ -595,7 +632,7 @@ return 1; // Wait for the valid status bit to go active. - for (count = 0; count < 10; count++) { + for (count = 0; count < 100; count++) { // Read the AC97 status register. // ACSTS = Status Register = 464h status = readl(card->pBA0 + BA0_ACSTS); @@ -705,12 +742,26 @@ 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)); + temp2 = readl(card->pBA0 + BA0_CWPR); + if(temp2 != CS4281_BA0_CPWR_DEFAULT) + { + writel(CS4281_BA0_CPWR_DEFAULT, card->pBA0 + BA0_CWPR); + temp2 = readl(card->pBA0 + BA0_CWPR); + if(temp2 != CS4281_BA0_CPWR_DEFAULT) + { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs4281: cs4281_hw_init() Invalid hardware - unable to configure CPWR (0x%x)\n", + temp2)); + return 1; + } + } 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")); + "cs4281: cs4281_hw_init() Invalid hardware - unable to configure CFLR (0x%x)\n", + temp2)); return 1; } } @@ -1023,15 +1074,6 @@ 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); @@ -1271,6 +1313,14 @@ pm->u32DacSR = readl(s->pBA0 + BA0_DACSR); pm->u32AdcSR = readl(s->pBA0 + BA0_ADCSR); +/* +* save the current hwptr, then stop the dac/adc +*/ + pm->u32hwptr_playback = readl(s->pBA0 + BA0_DCA0); + pm->u32hwptr_capture = readl(s->pBA0 + BA0_DCA1); + stop_dac(s); + stop_adc(s); + // // Loop through all of the PipeLines // @@ -1584,9 +1634,9 @@ #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. @@ -1642,7 +1692,7 @@ #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) { @@ -1691,7 +1741,7 @@ // --------------------------------------------------------------------- -#define DMABUF_MINORDER 1 // ==> min buffer size = 8K. +#define DMABUF_MINORDER 0 // ==> min buffer size = 8K. extern void dealloc_dmabuf(struct cs4281_state *s, struct dmabuf *db) @@ -1737,7 +1787,7 @@ * check for order within limits, but do not overwrite value, check * later for a fractional defaultorder (i.e. 100+). */ - if((defaultorder > 0) && (defaultorder < 12)) + if((defaultorder >= 0) && (defaultorder < 12)) df = defaultorder; else df = 1; @@ -1746,7 +1796,7 @@ 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))) + s->pcidev, PAGE_SIZE << order, &db->dmaaddr))) break; if (!db->rawbuf) { CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR @@ -1944,8 +1994,8 @@ len -= x; } CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO - "cs4281: clear_advance(): memset %d at %p for %d size \n", - (unsigned)c, ((char *) buf) + bptr, len)); + "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); } @@ -1971,17 +2021,16 @@ 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); + 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=%p hwptr=%d total_bytes=%d count=%d \n", - s, s->dma_adc.hwptr, s->dma_adc.total_bytes, s->dma_adc.count)); + "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 // @@ -2013,10 +2062,11 @@ // Continue to play silence until the _release. // CS_DBGOUT(CS_WAVE_WRITE, 6, printk(KERN_INFO - "cs4281: cs4281_update_ptr(): memset %d at %p for %d size \n", + "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, - s->dma_dac.rawbuf, s->dma_dac.dmasize)); + (unsigned)s->dma_dac.rawbuf, + s->dma_dac.dmasize)); memset(s->dma_dac.rawbuf, (s->prop_dac. fmt & (AFMT_U8 | AFMT_U16_LE)) ? @@ -2047,8 +2097,9 @@ } } CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO - "cs4281: cs4281_update_ptr(): s=%p hwptr=%d total_bytes=%d count=%d \n", - s, s->dma_dac.hwptr, s->dma_dac.total_bytes, s->dma_dac.count)); + "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)); } } @@ -2142,6 +2193,7 @@ static int mixer_ioctl(struct cs4281_state *s, unsigned int cmd, unsigned long arg) { + int return_mask=0; // 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] = { @@ -2179,7 +2231,8 @@ VALIDATE_STATE(s); CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO - "cs4281: mixer_ioctl(): s=%p cmd=0x%.8x\n", s, cmd)); + "cs4281: mixer_ioctl(): s=0x%.8x cmd=0x%.8x\n", + (unsigned) s, cmd)); #if CSDEBUG cs_printioctl(cmd); #endif @@ -2266,8 +2319,8 @@ } if (cmd == SOUND_MIXER_INFO) { mixer_info info; - strlcpy(info.id, "CS4281", sizeof(info.id)); - strlcpy(info.name, "Crystal CS4281", sizeof(info.name)); + 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; @@ -2275,8 +2328,8 @@ } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; - strlcpy(info.id, "CS4281", sizeof(info.id)); - strlcpy(info.name, "Crystal CS4281", sizeof(info.name)); + 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; @@ -2305,9 +2358,14 @@ 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 | + return_mask = (((SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME | - SOUND_MASK_LINE1, (int *) arg); + SOUND_MASK_LINE1) ) & ~recsrc_invalid); + + CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO + "cs4281: mixer_ioctl(): return_mask=0x%x recsrc_invalid=0x%x\n", + (unsigned)return_mask,(unsigned)recsrc_invalid)); + return put_user(return_mask, (int *) arg); case SOUND_MIXER_STEREODEVS: // Mixer channels supporting stereo return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | @@ -2344,6 +2402,13 @@ i = hweight32(val); // i = # bits on in val. if (i != 1) // One & only 1 bit must be on. return 0; + if(val & recsrc_invalid) + { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs4281: mixer_ioctl(): REC SOURCE select error - record source invalid on this system (0x%x)\n", + val)); + return -EINVAL; + } for (i = 0; i < sizeof(mixer_src) / sizeof(int); i++) { if (val == mixer_src[i]) { temp1 = (i << 8) | i; @@ -2588,7 +2653,6 @@ } 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")); @@ -2603,7 +2667,6 @@ (struct cs4281_state *) file->private_data; VALIDATE_STATE(s); - MOD_DEC_USE_COUNT; return 0; } @@ -2620,6 +2683,7 @@ // Mixer file operations struct. // ****************************************************************************************** static /*const */ struct file_operations cs4281_mixer_fops = { + .owner = THIS_MODULE, .llseek = no_llseek, .ioctl = cs4281_ioctl_mixdev, .open = cs4281_open_mixdev, @@ -2682,9 +2746,18 @@ int count; unsigned tmo; + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: drain_dac()+\n")); if (s->dma_dac.mapped) + { + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: drain_dac()- (mmap) 0\n")); + return 0; + } + if (s->ena & FMODE_WRITE) + add_wait_queue(&s->dma_dac.wait, &wait); + else return 0; - add_wait_queue(&s->dma_dac.wait, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&s->lock, flags); @@ -2697,6 +2770,8 @@ if (nonblock) { remove_wait_queue(&s->dma_dac.wait, &wait); current->state = TASK_RUNNING; + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: drain_dac()- -EBUSY\n")); return -EBUSY; } tmo = @@ -2712,7 +2787,13 @@ remove_wait_queue(&s->dma_dac.wait, &wait); current->state = TASK_RUNNING; if (signal_pending(current)) + { + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: drain_dac()- -ERESTARTSYS\n")); return -ERESTARTSYS; + } + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: drain_dac()- 0\n")); return 0; } @@ -2744,8 +2825,9 @@ CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs4281: CopySamples()+ ")); CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - " dst=%p src=%p count=%d iChannels=%d fmt=0x%x\n", - dst, src, (unsigned) count, (unsigned) iChannels, (unsigned) fmt)); + " 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 @@ -2825,9 +2907,9 @@ 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=%p\n", + "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, dest)); + (unsigned) cnt, (unsigned) dest)); if (cnt > s->dma_adc.dmasize) { cnt = s->dma_adc.dmasize; @@ -2872,7 +2954,7 @@ unsigned copied = 0; CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, - printk(KERN_INFO "cs4281: cs4281_read()+ %Zu \n", count)); + printk(KERN_INFO "cs4281: cs4281_read()+ %d \n", count)); VALIDATE_STATE(s); if (ppos != &file->f_pos) @@ -2895,7 +2977,7 @@ // while (count > 0) { CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - "_read() count>0 count=%Zu .count=%d .swptr=%d .hwptr=%d \n", + "_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); @@ -2952,10 +3034,11 @@ // the "cnt" is the number of bytes to read. CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO - "_read() copy_to cnt=%d count=%Zu ", cnt, count)); + "_read() copy_to cnt=%d count=%d ", cnt, count)); CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - " .dmasize=%d .count=%d buffer=%p ret=%Zd\n", - s->dma_adc.dmasize, s->dma_adc.count, buffer, ret)); + " .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)) @@ -2971,7 +3054,7 @@ start_adc(s); } CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, - printk(KERN_INFO "cs4281: cs4281_read()- %Zd\n", ret)); + printk(KERN_INFO "cs4281: cs4281_read()- %d\n", ret)); return ret; } @@ -2987,7 +3070,7 @@ int cnt; CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, - printk(KERN_INFO "cs4281: cs4281_write()+ count=%Zu\n", + printk(KERN_INFO "cs4281: cs4281_write()+ count=%d\n", count)); VALIDATE_STATE(s); @@ -3043,10 +3126,13 @@ start_dac(s); } CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, - printk(KERN_INFO "cs4281: cs4281_write()- %Zd\n", ret)); + printk(KERN_INFO "cs4281: cs4281_write()- %d\n", ret)); return ret; } +/* +* cs4281_poll(struct file *file, struct poll_table_struct *wait) +*/ static unsigned int cs4281_poll(struct file *file, struct poll_table_struct *wait) @@ -3057,7 +3143,7 @@ unsigned int mask = 0; CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, - printk(KERN_INFO "cs4281: cs4281_poll()+\n")); + printk(KERN_INFO "cs4281: cs4281_poll()+ wait=0x%x\n", (unsigned)wait)); VALIDATE_STATE(s); if (file->f_mode & FMODE_WRITE) { CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, @@ -3066,17 +3152,17 @@ 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) { + + } else 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)) + 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); - cs4281_update_ptr(s,CS_FALSE); if (file->f_mode & FMODE_WRITE) { if (s->dma_dac.mapped) { if (s->dma_dac.count >= @@ -3135,7 +3221,7 @@ // db = &s->dma_dac; - if (cs4x_pgoff(vma) != 0) + if (vm_pgoff(vma) != 0) return -EINVAL; size = vma->vm_end - vma->vm_start; if (size > (PAGE_SIZE << db->buforder)) @@ -3164,7 +3250,8 @@ int val, mapped, ret; CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO - "cs4281: cs4281_ioctl(): file=%p cmd=0x%.8x\n", file, cmd)); + "cs4281: cs4281_ioctl(): file=0x%.8x cmd=0x%.8x\n", + (unsigned) file, cmd)); #if CSDEBUG cs_printioctl(cmd); #endif @@ -3403,7 +3490,7 @@ if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s))) return val; spin_lock_irqsave(&s->lock, flags); - cs4281_update_ptr(s,CS_FALSE); + cs4281_update_ptr(s,CS_TRUE); abinfo.fragsize = s->dma_dac.fragsize; if (s->dma_dac.mapped) abinfo.bytes = s->dma_dac.dmasize; @@ -3426,7 +3513,7 @@ if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s))) return val; spin_lock_irqsave(&s->lock, flags); - cs4281_update_ptr(s,CS_FALSE); + cs4281_update_ptr(s,CS_TRUE); if (s->conversion) { abinfo.fragsize = s->dma_adc.fragsize / 2; abinfo.bytes = s->dma_adc.count / 2; @@ -3454,7 +3541,7 @@ if(!s->dma_dac.ready && prog_dmabuf_dac(s)) return 0; spin_lock_irqsave(&s->lock, flags); - cs4281_update_ptr(s,CS_FALSE); + cs4281_update_ptr(s,CS_TRUE); val = s->dma_dac.count; spin_unlock_irqrestore(&s->lock, flags); return put_user(val, (int *) arg); @@ -3465,7 +3552,7 @@ if(!s->dma_adc.ready && prog_dmabuf_adc(s)) return 0; spin_lock_irqsave(&s->lock, flags); - cs4281_update_ptr(s,CS_FALSE); + cs4281_update_ptr(s,CS_TRUE); cinfo.bytes = s->dma_adc.total_bytes; if (s->dma_adc.mapped) { cinfo.blocks = @@ -3500,7 +3587,7 @@ if(!s->dma_dac.ready && prog_dmabuf_dac(s)) return 0; spin_lock_irqsave(&s->lock, flags); - cs4281_update_ptr(s,CS_FALSE); + cs4281_update_ptr(s,CS_TRUE); cinfo.bytes = s->dma_dac.total_bytes; if (s->dma_dac.mapped) { cinfo.blocks = @@ -3537,6 +3624,10 @@ case SNDCTL_DSP_SETFRAGMENT: if (get_user(val, (int *) arg)) return -EFAULT; + + CS_DBGOUT(CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): Attempt to set fragsize=%d fragnum=%d\n", + 1 << (val & 0xffff), (val >> 16) & 0xffff )); return 0; // Say OK, but do nothing. case SNDCTL_DSP_SUBDIVIDE: @@ -3594,8 +3685,8 @@ (struct cs4281_state *) file->private_data; CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO - "cs4281: cs4281_release(): inode=%p file=%p f_mode=%d\n", - inode, file, file->f_mode)); + "cs4281: cs4281_release(): inode=0x%.8x file=0x%.8x f_mode=%d\n", + (unsigned) inode, (unsigned) file, file->f_mode)); VALIDATE_STATE(s); @@ -3607,7 +3698,6 @@ 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); @@ -3617,7 +3707,6 @@ s->open_mode &= ~FMODE_READ; up(&s->open_sem_adc); wake_up(&s->open_wait_adc); - MOD_DEC_USE_COUNT; } return 0; } @@ -3697,7 +3786,6 @@ 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 @@ -3718,7 +3806,6 @@ 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 @@ -3738,6 +3825,7 @@ // Wave (audio) file operations struct. // ****************************************************************************************** static /*const */ struct file_operations cs4281_audio_fops = { + .owner = THIS_MODULE, .llseek = no_llseek, .read = cs4281_read, .write = cs4281_write, @@ -4024,12 +4112,8 @@ 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); + 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; } @@ -4069,9 +4153,7 @@ current->state = TASK_RUNNING; } down(&s->open_sem); - s->open_mode &= - (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ | - FMODE_MIDI_WRITE); + 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. @@ -4080,7 +4162,6 @@ spin_unlock_irqrestore(&s->lock, flags); up(&s->open_sem); wake_up(&s->open_wait); - MOD_DEC_USE_COUNT; return 0; } @@ -4088,6 +4169,7 @@ // Midi file operations struct. // ****************************************************************************************** static /*const */ struct file_operations cs4281_midi_fops = { + .owner = THIS_MODULE, .llseek = no_llseek, .read = cs4281_midi_read, .write = cs4281_midi_write, @@ -4263,6 +4345,35 @@ } #endif +/* +* ss_vendor and ss_id must be setup prior to calling this routine. +* setup the invalid recording source bitmask, +* and also return a valid default initialization value as +* the return value; +*/ +static int cs4281_setup_record_src(struct cs4281_state *s) +{ + if(s->ss_vendor == PCI_VENDOR_ID_TOSHIBA) + { + if(s->ss_id == SS_ID_TOSHIBA_1640CDT) + { + CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO + "cs4281: cs4281_setup_record_src(): setting LINE invalid\n")); + recsrc_invalid |= SOUND_MASK_LINE; + } + } +/* +* only return a valid recsrc value here, default to something useful. +*/ + if(!(recsrc_invalid & SOUND_MASK_MIC)) + return(SOUND_MASK_MIC); + else if(!(recsrc_invalid & SOUND_MASK_LINE)) + return(SOUND_MASK_LINE); + else if(!(recsrc_invalid & SOUND_MASK_LINE1)) + return(SOUND_MASK_LINE1); + return 0; +} + static int __devinit cs4281_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) { @@ -4285,22 +4396,22 @@ } 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")); + 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")); + } + 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")); + 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")); @@ -4340,8 +4451,10 @@ CS_DBGOUT(CS_INIT, 2, printk(KERN_INFO - "cs4281: probe() BA0=0x%.8x BA1=0x%.8x pBA0=%p pBA1=%p \n", - (unsigned) temp1, (unsigned) temp2, s->pBA0, s->pBA1)); + "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", @@ -4388,22 +4501,32 @@ if (pmdev) { CS_DBGOUT(CS_INIT | CS_PM, 4, printk(KERN_INFO - "cs4281: probe() pm_register() succeeded (%p).\n", pmdev)); + "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 (%p).\n", pmdev)); + "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 + pci_read_config_word(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &s->ss_vendor); + pci_read_config_word(pcidev, PCI_SUBSYSTEM_ID, &s->ss_id); + printk(KERN_INFO "cs4281: Subsystem vendor/id (%04X:%04X) IRQ %d\n", + s->ss_vendor, s->ss_id, s->irq); + + if(!recsrc_invalid) + val = cs4281_setup_record_src(s); + else + val = SOUND_MASK_MIC; 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; @@ -4505,7 +4628,7 @@ } // --------------------------------------------------------------------- -MODULE_AUTHOR("gw boynton, audio@crystal.cirrus.com"); +MODULE_AUTHOR("gw boynton, pcaudio@crystal.cirrus.com"); MODULE_DESCRIPTION("Cirrus Logic CS4281 Driver"); MODULE_LICENSE("GPL"); @@ -4520,3 +4643,4 @@ return cs4281_init_module(); } #endif +#include "cs4281pm-24.c" diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/cs4281/cs4281_wrapper-24.c linux-2.6.0-test1-ac1/sound/oss/cs4281/cs4281_wrapper-24.c --- linux-2.6.0-test1/sound/oss/cs4281/cs4281_wrapper-24.c 2003-07-10 21:07:35.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/cs4281/cs4281_wrapper-24.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,41 +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 - -int cs4281_resume_null(struct pci_dev *pcidev) { return 0; } -int cs4281_suspend_null(struct pci_dev *pcidev, u32 state) { return 0; } - -#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 -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/cs4281/cs4281_wrapper.h linux-2.6.0-test1-ac1/sound/oss/cs4281/cs4281_wrapper.h --- linux-2.6.0-test1/sound/oss/cs4281/cs4281_wrapper.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/cs4281/cs4281_wrapper.h 2003-07-11 17:15:18.000000000 +0100 @@ -0,0 +1,54 @@ +/******************************************************************************* +* +* "cs4281_wrapper.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. +* 04/18/01 trw - rework entire wrapper logic. +* +*******************************************************************************/ +#ifndef __CS4281_WRAPPER_H +#define __CS4281_WRAPPER_H + +/* 2.4.x wrapper */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,9) +static int cs4281_null_suspend(struct pci_dev *pcidev, u32 unused) { return 0; } +static int cs4281_null_resume(struct pci_dev *pcidev) { return 0; } +#else +#define no_llseek cs4281_llseek +static loff_t cs4281_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} +void cs4281_null_suspend(struct pci_dev *pcidev) { return; } +void cs4281_null_resume(struct pci_dev *pcidev) { return; } +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,3) +/* Some versions of 2.4.2 resolve pci_set_dma_mask and some do not... +* but 2.4.0 definitely does not +*/ +#define pci_set_dma_mask(dev,data) 0; +#else +#endif +#define cs4x_mem_map_reserve(page) mem_map_reserve(page) +#define cs4x_mem_map_unreserve(page) mem_map_unreserve(page) + +#endif /* #ifndef __CS4281_WRAPPER_H */ diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/hal2.c linux-2.6.0-test1-ac1/sound/oss/hal2.c --- linux-2.6.0-test1/sound/oss/hal2.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/hal2.c 2003-07-11 16:40:47.000000000 +0100 @@ -0,0 +1,1498 @@ +/* + * Driver for HAL2 sound processors + * Copyright (c) 2001, 2002 Ladislav Michl + * + * Based on Ulf Carlsson's code. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Supported devices: + * /dev/dsp standard dsp device, (mostly) OSS compatible + * /dev/mixer standard mixer device, (mostly) OSS compatible + * + * BUGS: + * + Driver currently supports indigo mode only. + * + Recording doesn't work. I guess that it is caused by PBUS channel + * misconfiguration, but until I get relevant info I'm unable to fix it. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hal2.h" + +#if 0 +#define DEBUG(args...) printk(args) +#else +#define DEBUG(args...) +#endif + +#if 0 +#define DEBUG_MIX(args...) printk(args) +#else +#define DEBUG_MIX(args...) +#endif + +#define H2_INDIRECT_WAIT(regs) while (regs->isr & H2_ISR_TSTATUS); + +#define H2_READ_ADDR(addr) (addr | (1<<7)) +#define H2_WRITE_ADDR(addr) (addr) + +static char *hal2str = "HAL2 audio"; +static int ibuffers = 32; +static int obuffers = 32; + +/* I doubt anyone has a machine with two HAL2 cards. It's possible to + * have two HPC's, so it is probably possible to have two HAL2 cards. + * Try to deal with it, but note that it is not tested. + */ +#define MAXCARDS 2 +static hal2_card_t* hal2_card[MAXCARDS]; + +static const struct { + unsigned char idx:4, avail:1; +} mixtable[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = { H2_MIX_OUTPUT_ATT, 1 }, /* voice */ + [SOUND_MIXER_MIC] = { H2_MIX_INPUT_GAIN, 1 }, /* mic */ +}; + +#define H2_SUPPORTED_FORMATS (AFMT_S16_LE | AFMT_S16_BE) + +static inline void hal2_isr_write(hal2_card_t *hal2, u32 val) +{ + hal2->ctl_regs->isr = val; +} + +static inline u32 hal2_isr_look(hal2_card_t *hal2) +{ + return hal2->ctl_regs->isr; +} + +static inline u32 hal2_rev_look(hal2_card_t *hal2) +{ + return hal2->ctl_regs->rev; +} + +#if 0 +static u16 hal2_i_look16(hal2_card_t *hal2, u32 addr) +{ + hal2_ctl_regs_t *regs = hal2->ctl_regs; + + regs->iar = H2_READ_ADDR(addr); + H2_INDIRECT_WAIT(regs); + return (regs->idr0 & 0xffff); +} +#endif + +static u32 hal2_i_look32(hal2_card_t *hal2, u32 addr) +{ + u32 ret; + hal2_ctl_regs_t *regs = hal2->ctl_regs; + + regs->iar = H2_READ_ADDR(addr); + H2_INDIRECT_WAIT(regs); + ret = regs->idr0 & 0xffff; + regs->iar = H2_READ_ADDR(addr | 0x1); + H2_INDIRECT_WAIT(regs); + ret |= (regs->idr0 & 0xffff) << 16; + return ret; +} + +static void hal2_i_write16(hal2_card_t *hal2, u32 addr, u16 val) +{ + hal2_ctl_regs_t *regs = hal2->ctl_regs; + + regs->idr0 = val; + regs->idr1 = 0; + regs->idr2 = 0; + regs->idr3 = 0; + regs->iar = H2_WRITE_ADDR(addr); + H2_INDIRECT_WAIT(regs); +} + +static void hal2_i_write32(hal2_card_t *hal2, u32 addr, u32 val) +{ + hal2_ctl_regs_t *regs = hal2->ctl_regs; + + regs->idr0 = val & 0xffff; + regs->idr1 = val >> 16; + regs->idr2 = 0; + regs->idr3 = 0; + regs->iar = H2_WRITE_ADDR(addr); + H2_INDIRECT_WAIT(regs); +} + +static void hal2_i_setbit16(hal2_card_t *hal2, u32 addr, u16 bit) +{ + hal2_ctl_regs_t *regs = hal2->ctl_regs; + + regs->iar = H2_READ_ADDR(addr); + H2_INDIRECT_WAIT(regs); + regs->idr0 = regs->idr0 | bit; + regs->idr1 = 0; + regs->idr2 = 0; + regs->idr3 = 0; + regs->iar = H2_WRITE_ADDR(addr); + H2_INDIRECT_WAIT(regs); +} + +static void hal2_i_setbit32(hal2_card_t *hal2, u32 addr, u32 bit) +{ + u32 tmp; + hal2_ctl_regs_t *regs = hal2->ctl_regs; + + regs->iar = H2_READ_ADDR(addr); + H2_INDIRECT_WAIT(regs); + tmp = regs->idr0 | (regs->idr1 << 16) | bit; + regs->idr0 = tmp & 0xffff; + regs->idr1 = tmp >> 16; + regs->idr2 = 0; + regs->idr3 = 0; + regs->iar = H2_WRITE_ADDR(addr); + H2_INDIRECT_WAIT(regs); +} + +static void hal2_i_clearbit16(hal2_card_t *hal2, u32 addr, u16 bit) +{ + hal2_ctl_regs_t *regs = hal2->ctl_regs; + + regs->iar = H2_READ_ADDR(addr); + H2_INDIRECT_WAIT(regs); + regs->idr0 = regs->idr0 & ~bit; + regs->idr1 = 0; + regs->idr2 = 0; + regs->idr3 = 0; + regs->iar = H2_WRITE_ADDR(addr); + H2_INDIRECT_WAIT(regs); +} + +#if 0 +static void hal2_i_clearbit32(hal2_card_t *hal2, u32 addr, u32 bit) +{ + u32 tmp; + hal2_ctl_regs_t *regs = hal2->ctl_regs; + + regs->iar = H2_READ_ADDR(addr); + H2_INDIRECT_WAIT(regs); + tmp = (regs->idr0 | (regs->idr1 << 16)) & ~bit; + regs->idr0 = tmp & 0xffff; + regs->idr1 = tmp >> 16; + regs->idr2 = 0; + regs->idr3 = 0; + regs->iar = H2_WRITE_ADDR(addr); + H2_INDIRECT_WAIT(regs); +} +#endif + +#ifdef HAL2_DEBUG +static void hal2_dump_regs(hal2_card_t *hal2) +{ + printk("isr: %08hx ", hal2_isr_look(hal2)); + printk("rev: %08hx\n", hal2_rev_look(hal2)); + printk("relay: %04hx\n", hal2_i_look16(hal2, H2I_RELAY_C)); + printk("port en: %04hx ", hal2_i_look16(hal2, H2I_DMA_PORT_EN)); + printk("dma end: %04hx ", hal2_i_look16(hal2, H2I_DMA_END)); + printk("dma drv: %04hx\n", hal2_i_look16(hal2, H2I_DMA_DRV)); + printk("syn ctl: %04hx ", hal2_i_look16(hal2, H2I_SYNTH_C)); + printk("aesrx ctl: %04hx ", hal2_i_look16(hal2, H2I_AESRX_C)); + printk("aestx ctl: %04hx ", hal2_i_look16(hal2, H2I_AESTX_C)); + printk("dac ctl1: %04hx ", hal2_i_look16(hal2, H2I_ADC_C1)); + printk("dac ctl2: %08lx ", hal2_i_look32(hal2, H2I_ADC_C2)); + printk("adc ctl1: %04hx ", hal2_i_look16(hal2, H2I_DAC_C1)); + printk("adc ctl2: %08lx ", hal2_i_look32(hal2, H2I_DAC_C2)); + printk("syn map: %04hx\n", hal2_i_look16(hal2, H2I_SYNTH_MAP_C)); + printk("bres1 ctl1: %04hx ", hal2_i_look16(hal2, H2I_BRES1_C1)); + printk("bres1 ctl2: %04lx ", hal2_i_look32(hal2, H2I_BRES1_C2)); + printk("bres2 ctl1: %04hx ", hal2_i_look16(hal2, H2I_BRES2_C1)); + printk("bres2 ctl2: %04lx ", hal2_i_look32(hal2, H2I_BRES2_C2)); + printk("bres3 ctl1: %04hx ", hal2_i_look16(hal2, H2I_BRES3_C1)); + printk("bres3 ctl2: %04lx\n", hal2_i_look32(hal2, H2I_BRES3_C2)); +} +#endif + +static hal2_card_t* hal2_dsp_find_card(int minor) +{ + int i; + + for (i = 0; i < MAXCARDS; i++) + if (hal2_card[i] != NULL && hal2_card[i]->dev_dsp == minor) + return hal2_card[i]; + return NULL; +} + +static hal2_card_t* hal2_mixer_find_card(int minor) +{ + int i; + + for (i = 0; i < MAXCARDS; i++) + if (hal2_card[i] != NULL && hal2_card[i]->dev_mixer == minor) + return hal2_card[i]; + return NULL; +} + + +static void hal2_dac_interrupt(hal2_codec_t *dac) +{ + int running; + + spin_lock(&dac->lock); + + /* if tail buffer contains zero samples DMA stream was already + * stopped */ + running = dac->tail->info.cnt; + dac->tail->info.cnt = 0; + dac->tail->info.desc.cntinfo = HPCDMA_XIE | HPCDMA_EOX; + dma_cache_wback_inv((unsigned long) dac->tail, + sizeof(struct hpc_dma_desc)); + /* we just proccessed empty buffer, don't update tail pointer */ + if (running) + dac->tail = dac->tail->info.next; + + spin_unlock(&dac->lock); + + wake_up(&dac->dma_wait); +} + +static void hal2_adc_interrupt(hal2_codec_t *adc) +{ + int running; + + spin_lock(&adc->lock); + + /* if head buffer contains nonzero samples DMA stream was already + * stopped */ + running = !adc->head->info.cnt; + adc->head->info.cnt = H2_BUFFER_SIZE; + adc->head->info.desc.cntinfo = HPCDMA_XIE | HPCDMA_EOX; + dma_cache_wback_inv((unsigned long) adc->head, + sizeof(struct hpc_dma_desc)); + /* we just proccessed empty buffer, don't update head pointer */ + if (running) { + dma_cache_inv((unsigned long) adc->head->data, H2_BUFFER_SIZE); + adc->head = adc->head->info.next; + } + + spin_unlock(&adc->lock); + + wake_up(&adc->dma_wait); +} + +static irqreturn_t hal2_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + hal2_card_t *hal2 = (hal2_card_t*)dev_id; + + /* decide what caused this interrupt */ + if (hal2->dac.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) + hal2_dac_interrupt(&hal2->dac); + if (hal2->adc.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) + hal2_adc_interrupt(&hal2->adc); + return IRQ_HANDLED; +} + +static int hal2_compute_rate(hal2_codec_t *codec, unsigned int rate) +{ + unsigned short inc; + + /* We default to 44.1 kHz and if it isn't possible to fall back to + * 48.0 kHz with the needed adjustments of real_rate. + */ + + DEBUG("rate: %d\n", rate); + + /* Refer to CS4216 data sheet */ + if (rate < 4000) + rate = 4000; + if (rate > 50000) + rate = 50000; + + /* Note: This is NOT the way they set up the bresenham clock generators + * in the specification. I've tried to implement that method but it + * doesn't work. It's probably another silly bug in the spec. + * + * I accidently discovered this method while I was testing and it seems + * to work very well with all frequencies, and thee shall follow rule #1 + * of programming :-) + */ + + if (44100 % rate == 0) { + inc = 44100 / rate; + if (inc < 1) inc = 1; + codec->master = 44100; + } else { + inc = 48000 / rate; + if (inc < 1) inc = 1; + rate = 48000 / inc; + codec->master = 48000; + } + codec->inc = inc; + codec->mod = 1; + + DEBUG("real_rate: %d\n", rate); + + return rate; +} + +static void hal2_set_dac_rate(hal2_card_t *hal2) +{ + unsigned int master = hal2->dac.master; + int inc = hal2->dac.inc; + int mod = hal2->dac.mod; + + DEBUG("master: %d inc: %d mod: %d\n", master, inc, mod); + + hal2_i_write16(hal2, H2I_BRES1_C1, (master == 44100) ? 1 : 0); + hal2_i_write32(hal2, H2I_BRES1_C2, ((0xffff & (mod - inc - 1)) << 16) | 1); +} + +static void hal2_set_adc_rate(hal2_card_t *hal2) +{ + unsigned int master = hal2->adc.master; + int inc = hal2->adc.inc; + int mod = hal2->adc.mod; + + DEBUG("master: %d inc: %d mod: %d\n", master, inc, mod); + + hal2_i_write16(hal2, H2I_BRES2_C1, (master == 44100) ? 1 : 0); + hal2_i_write32(hal2, H2I_BRES2_C2, ((0xffff & (mod - inc - 1)) << 16) | 1); +} + +static void hal2_setup_dac(hal2_card_t *hal2) +{ + unsigned int fifobeg, fifoend, highwater, sample_size; + hal2_pbus_t *pbus = &hal2->dac.pbus; + + DEBUG("hal2_setup_dac\n"); + + /* Now we set up some PBUS information. The PBUS needs information about + * what portion of the fifo it will use. If it's receiving or + * transmitting, and finally whether the stream is little endian or big + * endian. The information is written later, on the start call. + */ + sample_size = 2 * hal2->dac.voices; + + /* Fifo should be set to hold exactly four samples. Highwater mark + * should be set to two samples. */ + highwater = (sample_size * 2) >> 1; /* halfwords */ + fifobeg = 0; /* playback is first */ + fifoend = (sample_size * 4) >> 3; /* doublewords */ + pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_LD | + (highwater << 8) | (fifobeg << 16) | (fifoend << 24); + /* We disable everything before we do anything at all */ + pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD; + hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX); + hal2_i_clearbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr)); + /* Setup the HAL2 for playback */ + hal2_set_dac_rate(hal2); + /* We are using 1st Bresenham clock generator for playback */ + hal2_i_write16(hal2, H2I_DAC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT) + | (1 << H2I_C1_CLKID_SHIFT) + | (hal2->dac.voices << H2I_C1_DATAT_SHIFT)); +} + +static void hal2_setup_adc(hal2_card_t *hal2) +{ + unsigned int fifobeg, fifoend, highwater, sample_size; + hal2_pbus_t *pbus = &hal2->adc.pbus; + + DEBUG("hal2_setup_adc\n"); + + sample_size = 2 * hal2->adc.voices; + + highwater = (sample_size * 2) >> 1; /* halfwords */ + fifobeg = (4 * 4) >> 3; /* record is second */ + fifoend = (4 * 4 + sample_size * 4) >> 3; /* doublewords */ + pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_RCV | HPC3_PDMACTRL_LD | + (highwater << 8) | (fifobeg << 16) | (fifoend << 24); + pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD; + hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR); + hal2_i_clearbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr)); + /* Setup the HAL2 for record */ + hal2_set_adc_rate(hal2); + /* We are using 2nd Bresenham clock generator for record */ + hal2_i_write16(hal2, H2I_ADC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT) + | (2 << H2I_C1_CLKID_SHIFT) + | (hal2->adc.voices << H2I_C1_DATAT_SHIFT)); +} + +static void hal2_start_dac(hal2_card_t *hal2) +{ + hal2_pbus_t *pbus = &hal2->dac.pbus; + + DEBUG("hal2_start_dac\n"); + + pbus->pbus->pbdma_dptr = PHYSADDR(hal2->dac.tail); + pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT; + + /* set endianess */ + if (hal2->dac.format & AFMT_S16_LE) + hal2_i_setbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECTX); + else + hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECTX); + /* set DMA bus */ + hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr)); + /* enable DAC */ + hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX); +} + +static void hal2_start_adc(hal2_card_t *hal2) +{ + hal2_pbus_t *pbus = &hal2->adc.pbus; + + DEBUG("hal2_start_adc\n"); + + pbus->pbus->pbdma_dptr = PHYSADDR(hal2->adc.head); + pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT; + + /* set endianess */ + if (hal2->adc.format & AFMT_S16_LE) + hal2_i_setbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECR); + else + hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECR); + /* set DMA bus */ + hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr)); + /* enable ADC */ + hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR); +} + +static inline void hal2_stop_dac(hal2_card_t *hal2) +{ + DEBUG("hal2_stop_dac\n"); + + hal2->dac.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD; + /* The HAL2 itself may remain enabled safely */ +} + +static inline void hal2_stop_adc(hal2_card_t *hal2) +{ + DEBUG("hal2_stop_adc\n"); + + hal2->adc.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD; +} + +#define hal2_alloc_dac_dmabuf(hal2) hal2_alloc_dmabuf(hal2, 1) +#define hal2_alloc_adc_dmabuf(hal2) hal2_alloc_dmabuf(hal2, 0) +static int hal2_alloc_dmabuf(hal2_card_t *hal2, int is_dac) +{ + int buffers, cntinfo; + hal2_buf_t *buf, *prev; + hal2_codec_t *codec; + + if (is_dac) { + codec = &hal2->dac; + buffers = obuffers; + cntinfo = HPCDMA_XIE | HPCDMA_EOX; + } else { + codec = &hal2->adc; + buffers = ibuffers; + cntinfo = HPCDMA_XIE | H2_BUFFER_SIZE; + } + + DEBUG("allocating %d DMA buffers.\n", buffers); + + buf = (hal2_buf_t*) get_zeroed_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + codec->head = buf; + codec->tail = buf; + + while (--buffers) { + buf->info.desc.pbuf = PHYSADDR(&buf->data); + buf->info.desc.cntinfo = cntinfo; + buf->info.cnt = 0; + prev = buf; + buf = (hal2_buf_t*) get_zeroed_page(GFP_KERNEL); + if (!buf) { + printk("HAL2: Not enough memory for DMA buffer.\n"); + buf = codec->head; + while (buf) { + prev = buf; + free_page((unsigned long) buf); + buf = prev->info.next; + } + return -ENOMEM; + } + prev->info.next = buf; + prev->info.desc.pnext = PHYSADDR(buf); + /* The PBUS can prolly not read this stuff when it's in + * the cache so we have to flush it back to main memory + */ + dma_cache_wback_inv((unsigned long) prev, PAGE_SIZE); + } + buf->info.desc.pbuf = PHYSADDR(&buf->data); + buf->info.desc.cntinfo = cntinfo; + buf->info.cnt = 0; + buf->info.next = codec->head; + buf->info.desc.pnext = PHYSADDR(codec->head); + dma_cache_wback_inv((unsigned long) buf, PAGE_SIZE); + + return 0; +} + +#define hal2_free_dac_dmabuf(hal2) hal2_free_dmabuf(hal2, 1) +#define hal2_free_adc_dmabuf(hal2) hal2_free_dmabuf(hal2, 0) +static void hal2_free_dmabuf(hal2_card_t *hal2, int is_dac) +{ + hal2_buf_t *buf, *next; + hal2_codec_t *codec = (is_dac) ? &hal2->dac : &hal2->adc; + + if (!codec->head) + return; + + buf = codec->head->info.next; + codec->head->info.next = NULL; + while (buf) { + next = buf->info.next; + free_page((unsigned long) buf); + buf = next; + } + codec->head = codec->tail = NULL; +} + +/* + * Add 'count' bytes to 'buffer' from DMA ring buffers. Return number of + * bytes added or -EFAULT if copy_from_user failed. + */ +static int hal2_get_buffer(hal2_card_t *hal2, char *buffer, int count) +{ + unsigned long flags; + int size, ret = 0; + hal2_codec_t *adc = &hal2->adc; + + spin_lock_irqsave(&adc->lock, flags); + + DEBUG("getting %d bytes ", count); + + /* enable DMA stream if there are no data */ + if (!(adc->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT) && + adc->tail->info.cnt == 0) + hal2_start_adc(hal2); + + DEBUG("... "); + + while (adc->tail->info.cnt > 0 && count > 0) { + size = min(adc->tail->info.cnt, count); + spin_unlock_irqrestore(&adc->lock, flags); + + if (copy_to_user(buffer, &adc->tail->data[H2_BUFFER_SIZE-size], + size)) { + ret = -EFAULT; + goto out; + } + + spin_lock_irqsave(&adc->lock, flags); + + adc->tail->info.cnt -= size; + /* buffer is empty, update tail pointer */ + if (adc->tail->info.cnt == 0) { + adc->tail->info.desc.cntinfo = HPCDMA_XIE | + H2_BUFFER_SIZE; + dma_cache_wback_inv((unsigned long) adc->tail, + sizeof(struct hpc_dma_desc)); + adc->tail = adc->tail->info.next; + /* enable DMA stream again if needed */ + if (!(adc->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT)) + hal2_start_adc(hal2); + + } + buffer += size; + ret += size; + count -= size; + + DEBUG("(%d) ", size); + } + spin_unlock_irqrestore(&adc->lock, flags); +out: + DEBUG("\n"); + + return ret; +} + +/* + * Add 'count' bytes from 'buffer' to DMA ring buffers. Return number of + * bytes added or -EFAULT if copy_from_user failed. + */ +static int hal2_add_buffer(hal2_card_t *hal2, char *buffer, int count) +{ + unsigned long flags; + int size, ret = 0; + hal2_codec_t *dac = &hal2->dac; + + spin_lock_irqsave(&dac->lock, flags); + + DEBUG("adding %d bytes ", count); + + while (dac->head->info.cnt == 0 && count > 0) { + size = min((int)H2_BUFFER_SIZE, count); + spin_unlock_irqrestore(&dac->lock, flags); + + if (copy_from_user(dac->head->data, buffer, size)) { + ret = -EFAULT; + goto out; + } + spin_lock_irqsave(&dac->lock, flags); + + dac->head->info.desc.cntinfo = size | HPCDMA_XIE; + dac->head->info.cnt = size; + dma_cache_wback_inv((unsigned long) dac->head, + size + PAGE_SIZE - H2_BUFFER_SIZE); + buffer += size; + ret += size; + count -= size; + dac->head = dac->head->info.next; + + DEBUG("(%d) ", size); + } + if (!(dac->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT) && ret > 0) + hal2_start_dac(hal2); + + spin_unlock_irqrestore(&dac->lock, flags); +out: + DEBUG("\n"); + + return ret; +} + +#define hal2_reset_dac_pointer(hal2) hal2_reset_pointer(hal2, 1) +#define hal2_reset_adc_pointer(hal2) hal2_reset_pointer(hal2, 0) +static void hal2_reset_pointer(hal2_card_t *hal2, int is_dac) +{ + hal2_codec_t *codec = (is_dac) ? &hal2->dac : &hal2->adc; + + DEBUG("hal2_reset_pointer\n"); + + codec->tail = codec->head; + do { + codec->tail->info.desc.cntinfo = HPCDMA_XIE | (is_dac) ? + HPCDMA_EOX : H2_BUFFER_SIZE; + codec->tail->info.cnt = 0; + dma_cache_wback_inv((unsigned long) codec->tail, + sizeof(struct hpc_dma_desc)); + codec->tail = codec->tail->info.next; + } while (codec->tail != codec->head); +} + +static int hal2_sync_dac(hal2_card_t *hal2) +{ + DECLARE_WAITQUEUE(wait, current); + hal2_codec_t *dac = &hal2->dac; + int ret = 0; + signed long timeout = 1000 * H2_BUFFER_SIZE * 2 * dac->voices * + HZ / dac->sample_rate / 900; + + down(&dac->sem); + + while (dac->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT) { + add_wait_queue(&dac->dma_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + if (!schedule_timeout(timeout)) + /* We may get bogus timeout when system is + * heavily loaded */ + if (dac->tail->info.cnt) { + printk("HAL2: timeout...\n"); + ret = -ETIME; + } + if (signal_pending(current)) + ret = -ERESTARTSYS; + if (ret) { + hal2_stop_dac(hal2); + hal2_reset_dac_pointer(hal2); + } + remove_wait_queue(&dac->dma_wait, &wait); + } + + up(&dac->sem); + + return ret; +} + +static int hal2_write_mixer(hal2_card_t *hal2, int index, int vol) +{ + unsigned int l, r; + + DEBUG_MIX("mixer %d write\n", index); + + if (index >= SOUND_MIXER_NRDEVICES || !mixtable[index].avail) + return -EINVAL; + + r = (vol >> 8) & 0xff; + if (r > 100) + r = 100; + l = vol & 0xff; + if (l > 100) + l = 100; + + hal2->mixer.volume[mixtable[index].idx] = l | (r << 8); + + switch (mixtable[index].idx) { + case H2_MIX_OUTPUT_ATT: { + + DEBUG_MIX("output attenuator %d,%d\n", l, r); + + if (r | l) { + unsigned int tmp = hal2_i_look32(hal2, H2I_DAC_C2); + + tmp &= ~(H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE); + + /* Attenuator has five bits */ + l = (31 * (100 - l) / 99); + r = (31 * (100 - r) / 99); + + DEBUG_MIX("left: %d, right %d\n", l, r); + + tmp |= (l << H2I_C2_L_ATT_SHIFT) & H2I_C2_L_ATT_M; + tmp |= (r << H2I_C2_R_ATT_SHIFT) & H2I_C2_R_ATT_M; + hal2_i_write32(hal2, H2I_DAC_C2, tmp); + } else + hal2_i_setbit32(hal2, H2I_DAC_C2, H2I_C2_MUTE); + } + case H2_MIX_INPUT_GAIN: { + /* TODO */ + } + } + return 0; +} + +static void hal2_init_mixer(hal2_card_t *hal2) +{ + int i; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + hal2_write_mixer(hal2, i, 100 | (100 << 8)); + +} + +static int hal2_mixer_ioctl(hal2_card_t *hal2, unsigned int cmd, + unsigned long arg) +{ + int val; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + + strncpy(info.id, hal2str, sizeof(info.id)); + strncpy(info.name, hal2str, sizeof(info.name)); + info.modify_counter = hal2->mixer.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, hal2str, sizeof(info.id)); + strncpy(info.name, hal2str, 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)) { + /* Give the current record source */ + case SOUND_MIXER_RECSRC: + val = 0; /* FIXME */ + break; + /* Give the supported mixers, all of them support stereo */ + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_STEREODEVS: { + int i; + + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].avail) + val |= 1 << i; + break; + } + /* Arg contains a bit for each supported recording source */ + case SOUND_MIXER_RECMASK: + val = 0; + break; + case SOUND_MIXER_CAPS: + val = 0; + break; + /* Read a specific mixer */ + default: { + int i = _IOC_NR(cmd); + + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) + return -EINVAL; + val = hal2->mixer.volume[mixtable[i].idx]; + break; + } + } + return put_user(val, (int *)arg); + } + + if (_IOC_DIR(cmd) != (_IOC_WRITE|_IOC_READ)) + return -EINVAL; + + hal2->mixer.modcnt++; + + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (_IOC_NR(cmd)) { + /* Arg contains a bit for each recording source */ + case SOUND_MIXER_RECSRC: + return 0; /* FIXME */ + default: + return hal2_write_mixer(hal2, _IOC_NR(cmd), val); + } + + return 0; +} + +static int hal2_open_mixdev(struct inode *inode, struct file *file) +{ + hal2_card_t *hal2 = hal2_mixer_find_card(MINOR(inode->i_rdev)); + + if (hal2) { + file->private_data = hal2; + return 0; + } + return -ENODEV; +} + +static int hal2_release_mixdev(struct inode *inode, struct file *file) +{ + return 0; +} + +static int hal2_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return hal2_mixer_ioctl((hal2_card_t *)file->private_data, cmd, arg); +} + + +static int hal2_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int val; + hal2_card_t *hal2 = (hal2_card_t *) file->private_data; + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return hal2_sync_dac(hal2); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_MULTI, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_READ) { + hal2_stop_adc(hal2); + hal2_reset_adc_pointer(hal2); + } + if (file->f_mode & FMODE_WRITE) { + hal2_stop_dac(hal2); + hal2_reset_dac_pointer(hal2); + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + hal2_stop_adc(hal2); + val = hal2_compute_rate(&hal2->adc, val); + hal2->adc.sample_rate = val; + hal2_set_adc_rate(hal2); + } + if (file->f_mode & FMODE_WRITE) { + hal2_stop_dac(hal2); + val = hal2_compute_rate(&hal2->dac, val); + hal2->dac.sample_rate = val; + hal2_set_dac_rate(hal2); + } + return put_user(val, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + hal2_stop_adc(hal2); + hal2->adc.voices = (val) ? 2 : 1; + hal2_setup_adc(hal2); + } + if (file->f_mode & FMODE_WRITE) { + hal2_stop_dac(hal2); + hal2->dac.voices = (val) ? 2 : 1; + hal2_setup_dac(hal2); + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + if (file->f_mode & FMODE_READ) { + hal2_stop_adc(hal2); + hal2->adc.voices = (val == 1) ? 1 : 2; + hal2_setup_adc(hal2); + } + if (file->f_mode & FMODE_WRITE) { + hal2_stop_dac(hal2); + hal2->dac.voices = (val == 1) ? 1 : 2; + hal2_setup_dac(hal2); + } + } + val = -EINVAL; + if (file->f_mode & FMODE_READ) + val = hal2->adc.voices; + if (file->f_mode & FMODE_WRITE) + val = hal2->dac.voices; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(H2_SUPPORTED_FORMATS, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (!(val & H2_SUPPORTED_FORMATS)) + return -EINVAL; + if (file->f_mode & FMODE_READ) { + hal2_stop_adc(hal2); + hal2->adc.format = val; + hal2_setup_adc(hal2); + } + if (file->f_mode & FMODE_WRITE) { + hal2_stop_dac(hal2); + hal2->dac.format = val; + hal2_setup_dac(hal2); + } + } else { + val = -EINVAL; + if (file->f_mode & FMODE_READ) + val = hal2->adc.format; + if (file->f_mode & FMODE_WRITE) + val = hal2->dac.format; + } + return put_user(val, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETOSPACE: { + unsigned long flags; + audio_buf_info info; + hal2_buf_t *buf; + hal2_codec_t *dac = &hal2->dac; + + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&dac->lock, flags); + info.fragments = 0; + buf = dac->head; + while (buf->info.cnt == 0 && buf != dac->tail) { + info.fragments++; + buf = buf->info.next; + } + spin_unlock_irqrestore(&dac->lock, flags); + + info.fragstotal = obuffers; + info.fragsize = H2_BUFFER_SIZE; + info.bytes = info.fragsize * info.fragments; + + return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0; + } + + case SNDCTL_DSP_GETISPACE: { + unsigned long flags; + audio_buf_info info; + hal2_buf_t *buf; + hal2_codec_t *adc = &hal2->adc; + + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + spin_lock_irqsave(&adc->lock, flags); + info.fragments = 0; + info.bytes = 0; + buf = adc->tail; + while (buf->info.cnt > 0 && buf != adc->head) { + info.fragments++; + info.bytes += buf->info.cnt; + buf = buf->info.next; + } + spin_unlock_irqrestore(&adc->lock, flags); + + info.fragstotal = ibuffers; + info.fragsize = H2_BUFFER_SIZE; + + return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0; + } + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + return put_user(H2_BUFFER_SIZE, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + return 0; + + case SOUND_PCM_READ_RATE: + val = -EINVAL; + if (file->f_mode & FMODE_READ) + val = hal2->adc.sample_rate; + if (file->f_mode & FMODE_WRITE) + val = hal2->dac.sample_rate; + return put_user(val, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + val = -EINVAL; + if (file->f_mode & FMODE_READ) + val = hal2->adc.voices; + if (file->f_mode & FMODE_WRITE) + val = hal2->dac.voices; + return put_user(val, (int *)arg); + + case SOUND_PCM_READ_BITS: + val = 16; + return put_user(val, (int *)arg); + } + + return hal2_mixer_ioctl(hal2, cmd, arg); +} + +static ssize_t hal2_read(struct file *file, char *buffer, + size_t count, loff_t *ppos) +{ + ssize_t err; + hal2_card_t *hal2 = (hal2_card_t *) file->private_data; + hal2_codec_t *adc = &hal2->adc; + + if (count == 0) + return 0; + if (ppos != &file->f_pos) + return -ESPIPE; + + down(&adc->sem); + + if (file->f_flags & O_NONBLOCK) { + err = hal2_get_buffer(hal2, buffer, count); + err = err == 0 ? -EAGAIN : err; + } else { + do { + /* ~10% longer */ + signed long timeout = 1000 * H2_BUFFER_SIZE * + 2 * adc->voices * HZ / adc->sample_rate / 900; + DECLARE_WAITQUEUE(wait, current); + ssize_t cnt = 0; + + err = hal2_get_buffer(hal2, buffer, count); + if (err > 0) { + count -= err; + cnt += err; + buffer += err; + err = cnt; + } + if (count > 0 && err >= 0) { + add_wait_queue(&adc->dma_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + /* Well, it is possible, that interrupt already + * arrived. Hmm, shit happens, we have one more + * buffer filled ;) */ + if (!schedule_timeout(timeout)) + /* We may get bogus timeout when system + * is heavily loaded */ + if (!adc->tail->info.cnt) { + printk("HAL2: timeout...\n"); + hal2_stop_adc(hal2); + hal2_reset_adc_pointer(hal2); + err = -EAGAIN; + } + if (signal_pending(current)) + err = -ERESTARTSYS; + remove_wait_queue(&adc->dma_wait, &wait); + } + } while (count > 0 && err >= 0); + + } + + up(&adc->sem); + + return err; +} + +static ssize_t hal2_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + ssize_t err; + char *buf = (char*) buffer; + hal2_card_t *hal2 = (hal2_card_t *) file->private_data; + hal2_codec_t *dac = &hal2->dac; + + if (count == 0) + return 0; + if (ppos != &file->f_pos) + return -ESPIPE; + + down(&dac->sem); + + if (file->f_flags & O_NONBLOCK) { + err = hal2_add_buffer(hal2, buf, count); + err = err == 0 ? -EAGAIN : err; + } else { + do { + /* ~10% longer */ + signed long timeout = 1000 * H2_BUFFER_SIZE * + 2 * dac->voices * HZ / dac->sample_rate / 900; + DECLARE_WAITQUEUE(wait, current); + ssize_t cnt = 0; + + err = hal2_add_buffer(hal2, buf, count); + if (err > 0) { + count -= err; + cnt += err; + buf += err; + err = cnt; + } + if (count > 0 && err >= 0) { + add_wait_queue(&dac->dma_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + /* Well, it is possible, that interrupt already + * arrived. Hmm, shit happens, we have one more + * buffer free ;) */ + if (!schedule_timeout(timeout)) + /* We may get bogus timeout when system + * is heavily loaded */ + if (dac->head->info.cnt) { + printk("HAL2: timeout...\n"); + hal2_stop_dac(hal2); + hal2_reset_dac_pointer(hal2); + err = -EAGAIN; + } + if (signal_pending(current)) + err = -ERESTARTSYS; + remove_wait_queue(&dac->dma_wait, &wait); + } + } while (count > 0 && err >= 0); + } + + up(&dac->sem); + + return err; +} + +static unsigned int hal2_poll(struct file *file, struct poll_table_struct *wait) +{ + unsigned long flags; + unsigned int mask = 0; + hal2_card_t *hal2 = (hal2_card_t *) file->private_data; + + if (file->f_mode & FMODE_READ) { + hal2_codec_t *adc = &hal2->adc; + + poll_wait(file, &hal2->adc.dma_wait, wait); + spin_lock_irqsave(&adc->lock, flags); + if (adc->tail->info.cnt > 0) + mask |= POLLIN; + spin_unlock_irqrestore(&adc->lock, flags); + } + + if (file->f_mode & FMODE_WRITE) { + hal2_codec_t *dac = &hal2->dac; + + poll_wait(file, &dac->dma_wait, wait); + spin_lock_irqsave(&dac->lock, flags); + if (dac->head->info.cnt == 0) + mask |= POLLOUT; + spin_unlock_irqrestore(&dac->lock, flags); + } + + return mask; +} + +static int hal2_open(struct inode *inode, struct file *file) +{ + int err; + hal2_card_t *hal2 = hal2_dsp_find_card(MINOR(inode->i_rdev)); + + DEBUG("opening audio device.\n"); + + if (!hal2) { + printk("HAL2: Whee?! Open door and go away!\n"); + return -ENODEV; + } + file->private_data = hal2; + + if (file->f_mode & FMODE_READ) { + if (hal2->adc.usecount) + return -EBUSY; + + /* OSS spec wanted us to use 8 bit, 8 kHz mono by default, + * but HAL2 can't do 8bit audio */ + hal2->adc.format = AFMT_S16_BE; + hal2->adc.voices = 1; + hal2->adc.sample_rate = hal2_compute_rate(&hal2->adc, 8000); + hal2_set_adc_rate(hal2); + + /* alloc DMA buffers */ + err = hal2_alloc_adc_dmabuf(hal2); + if (err) + return err; + hal2_setup_adc(hal2); + + hal2->adc.usecount++; + } + + if (file->f_mode & FMODE_WRITE) { + if (hal2->dac.usecount) + return -EBUSY; + + hal2->dac.format = AFMT_S16_BE; + hal2->dac.voices = 1; + hal2->dac.sample_rate = hal2_compute_rate(&hal2->dac, 8000); + hal2_set_dac_rate(hal2); + + /* alloc DMA buffers */ + err = hal2_alloc_dac_dmabuf(hal2); + if (err) + return err; + hal2_setup_dac(hal2); + + hal2->dac.usecount++; + } + + return 0; +} + +static int hal2_release(struct inode *inode, struct file *file) +{ + hal2_card_t *hal2 = (hal2_card_t *) file->private_data; + + if (file->f_mode & FMODE_READ) { + hal2_stop_adc(hal2); + hal2_free_adc_dmabuf(hal2); + hal2->adc.usecount--; + } + + if (file->f_mode & FMODE_WRITE) { + hal2_sync_dac(hal2); + hal2_free_dac_dmabuf(hal2); + hal2->dac.usecount--; + } + + return 0; +} + +static struct file_operations hal2_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: hal2_read, + write: hal2_write, + poll: hal2_poll, + ioctl: hal2_ioctl, + open: hal2_open, + release: hal2_release, +}; + +static struct file_operations hal2_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: hal2_ioctl_mixdev, + open: hal2_open_mixdev, + release: hal2_release_mixdev, +}; + +static int hal2_request_irq(hal2_card_t *hal2, int irq) +{ + unsigned long flags; + int ret = 0; + + save_and_cli(flags); + if (request_irq(irq, hal2_interrupt, SA_SHIRQ, hal2str, hal2)) { + printk(KERN_ERR "HAL2: Can't get irq %d\n", irq); + ret = -EAGAIN; + } + restore_flags(flags); + return ret; +} + +static int hal2_alloc_resources(hal2_card_t *hal2, struct hpc3_regs *hpc3) +{ + hal2_pbus_t *pbus; + + pbus = &hal2->dac.pbus; + pbus->pbusnr = 0; + pbus->pbus = &hpc3->pbdma[pbus->pbusnr]; + /* The spec says that we should write 0x08248844 but that's WRONG. HAL2 + * does 8 bit DMA, not 16 bit even if it generates 16 bit audio. */ + hpc3->pbus_dmacfgs[pbus->pbusnr][0] = 0x08208844; /* Magic :-) */ + + pbus = &hal2->adc.pbus; + pbus->pbusnr = 1; + pbus->pbus = &hpc3->pbdma[pbus->pbusnr]; + hpc3->pbus_dmacfgs[pbus->pbusnr][0] = 0x08208844; /* Magic :-) */ + + return hal2_request_irq(hal2, SGI_HPCDMA_IRQ); +} + +static void hal2_init_codec(hal2_codec_t *codec) +{ + init_waitqueue_head(&codec->dma_wait); + init_MUTEX(&codec->sem); + spin_lock_init(&codec->lock); +} + +static void hal2_free_resources(hal2_card_t *hal2) +{ + free_irq(SGI_HPCDMA_IRQ, hal2); +} + +static int hal2_detect(hal2_card_t *hal2) +{ + unsigned short board, major, minor; + unsigned short rev; + + /* reset HAL2 */ + hal2_isr_write(hal2, 0); + + /* release reset */ + hal2_isr_write(hal2, H2_ISR_GLOBAL_RESET_N | H2_ISR_CODEC_RESET_N); + + hal2_i_write16(hal2, H2I_RELAY_C, H2I_RELAY_C_STATE); + + if ((rev = hal2_rev_look(hal2)) & H2_REV_AUDIO_PRESENT) { + DEBUG("HAL2: no device detected, rev: 0x%04hx\n", rev); + return -ENODEV; + } + + board = (rev & H2_REV_BOARD_M) >> 12; + major = (rev & H2_REV_MAJOR_CHIP_M) >> 4; + minor = (rev & H2_REV_MINOR_CHIP_M); + + printk("SGI HAL2 Processor revision %i.%i.%i detected\n", + board, major, minor); + + if (board != 4 || major != 1 || minor != 0) + printk( "Other revision than 4.1.0 detected. " + "Your card is probably unsupported\n"); + + return 0; +} + +static int hal2_init_card(hal2_card_t **phal2, struct hpc3_regs *hpc3, + unsigned long hpc3_base) +{ + int ret = 0; + hal2_card_t *hal2; + + hal2 = (hal2_card_t *) kmalloc(sizeof(hal2_card_t), GFP_KERNEL); + if (!hal2) + return -ENOMEM; + memset(hal2, 0, sizeof(hal2_card_t)); + + hal2->ctl_regs = (hal2_ctl_regs_t *) KSEG1ADDR(hpc3_base + H2_CTL_PIO); + hal2->aes_regs = (hal2_aes_regs_t *) KSEG1ADDR(hpc3_base + H2_AES_PIO); + hal2->vol_regs = (hal2_vol_regs_t *) KSEG1ADDR(hpc3_base + H2_VOL_PIO); + hal2->syn_regs = (hal2_syn_regs_t *) KSEG1ADDR(hpc3_base + H2_SYN_PIO); + + if (hal2_detect(hal2) < 0) { + printk("HAL2 audio processor not found\n"); + ret = -ENODEV; + goto fail1; + } + + hal2_init_codec(&hal2->dac); + hal2_init_codec(&hal2->adc); + + ret = hal2_alloc_resources(hal2, hpc3); + if (ret) + goto fail1; + + hal2_init_mixer(hal2); + + hal2->dev_dsp = register_sound_dsp(&hal2_audio_fops, -1); + if (hal2->dev_dsp < 0) { + ret = hal2->dev_dsp; + goto fail2; + } + + hal2->dev_mixer = register_sound_mixer(&hal2_mixer_fops, -1); + if (hal2->dev_mixer < 0) { + ret = hal2->dev_mixer; + goto fail3; + } + + *phal2 = hal2; + return 0; +fail3: + unregister_sound_dsp(hal2->dev_dsp); +fail2: + hal2_free_resources(hal2); +fail1: + kfree(hal2); + + return ret; +} + +/* + * We are assuming only one HAL2 card. If you ever meet machine with more than + * one, tell immediately about it to someone. Preferably to me. --ladis + */ +static int __init init_hal2(void) +{ + int i; + + for (i = 0; i < MAXCARDS; i++) + hal2_card[i] = NULL; + + return hal2_init_card(&hal2_card[0], hpc3c0, HPC3_CHIP0_PBASE); +} + +static void __exit exit_hal2(void) +{ + int i; + + for (i = 0; i < MAXCARDS; i++) + if (hal2_card[i]) { + hal2_free_resources(hal2_card[i]); + unregister_sound_dsp(hal2_card[i]->dev_dsp); + unregister_sound_mixer(hal2_card[i]->dev_mixer); + kfree(hal2_card[i]); + } +} + +module_init(init_hal2); +module_exit(exit_hal2); + +MODULE_DESCRIPTION("OSS compatible driver for SGI HAL2 audio"); +MODULE_AUTHOR("Ladislav Michl"); +MODULE_LICENSE("GPL"); diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/hal2.h linux-2.6.0-test1-ac1/sound/oss/hal2.h --- linux-2.6.0-test1/sound/oss/hal2.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/hal2.h 2003-07-11 16:39:51.000000000 +0100 @@ -0,0 +1,328 @@ +#ifndef __HAL2_H +#define __HAL2_H + +/* + * Driver for HAL2 sound processors + * Copyright (c) 1999 Ulf Carlsson + * Copyright (c) 2001 Ladislav Michl + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include + +#define H2_HAL2_BASE 0x58000 +#define H2_CTL_PIO (H2_HAL2_BASE + 0 * 0x400) +#define H2_AES_PIO (H2_HAL2_BASE + 1 * 0x400) +#define H2_VOL_PIO (H2_HAL2_BASE + 2 * 0x400) +#define H2_SYN_PIO (H2_HAL2_BASE + 3 * 0x400) + +/* Indirect status register */ + +#define H2_ISR_TSTATUS 0x01 /* RO: transaction status 1=busy */ +#define H2_ISR_USTATUS 0x02 /* RO: utime status bit 1=armed */ +#define H2_ISR_QUAD_MODE 0x04 /* codec mode 0=indigo 1=quad */ +#define H2_ISR_GLOBAL_RESET_N 0x08 /* chip global reset 0=reset */ +#define H2_ISR_CODEC_RESET_N 0x10 /* codec/synth reset 0=reset */ + +/* Revision register */ + +#define H2_REV_AUDIO_PRESENT 0x8000 /* RO: audio present 0=present */ +#define H2_REV_BOARD_M 0x7000 /* RO: bits 14:12, board revision */ +#define H2_REV_MAJOR_CHIP_M 0x00F0 /* RO: bits 7:4, major chip revision */ +#define H2_REV_MINOR_CHIP_M 0x000F /* RO: bits 3:0, minor chip revision */ + +/* Indirect address register */ + +/* + * Address of indirect internal register to be accessed. A write to this + * register initiates read or write access to the indirect registers in the + * HAL2. Note that there af four indirect data registers for write access to + * registers larger than 16 byte. + */ + +#define H2_IAR_TYPE_M 0xF000 /* bits 15:12, type of functional */ + /* block the register resides in */ + /* 1=DMA Port */ + /* 9=Global DMA Control */ + /* 2=Bresenham */ + /* 3=Unix Timer */ +#define H2_IAR_NUM_M 0x0F00 /* bits 11:8 instance of the */ + /* blockin which the indirect */ + /* register resides */ + /* If IAR_TYPE_M=DMA Port: */ + /* 1=Synth In */ + /* 2=AES In */ + /* 3=AES Out */ + /* 4=DAC Out */ + /* 5=ADC Out */ + /* 6=Synth Control */ + /* If IAR_TYPE_M=Global DMA Control: */ + /* 1=Control */ + /* If IAR_TYPE_M=Bresenham: */ + /* 1=Bresenham Clock Gen 1 */ + /* 2=Bresenham Clock Gen 2 */ + /* 3=Bresenham Clock Gen 3 */ + /* If IAR_TYPE_M=Unix Timer: */ + /* 1=Unix Timer */ +#define H2_IAR_ACCESS_SELECT 0x0080 /* 1=read 0=write */ +#define H2_IAR_PARAM 0x000C /* Parameter Select */ +#define H2_IAR_RB_INDEX_M 0x0003 /* Read Back Index */ + /* 00:word0 */ + /* 01:word1 */ + /* 10:word2 */ + /* 11:word3 */ +/* + * HAL2 internal addressing + * + * The HAL2 has "indirect registers" (idr) which are accessed by writing to the + * Indirect Data registers. Write the address to the Indirect Address register + * to transfer the data. + * + * We define the H2IR_* to the read address and H2IW_* to the write address and + * H2I_* to be fields in whatever register is referred to. + * + * When we write to indirect registers which are larger than one word (16 bit) + * we have to fill more than one indirect register before writing. When we read + * back however we have to read several times, each time with different Read + * Back Indexes (there are defs for doing this easily). + */ + +/* + * Relay Control + */ +#define H2I_RELAY_C 0x9100 +#define H2I_RELAY_C_STATE 0x01 /* state of RELAY pin signal */ + +/* DMA port enable */ + +#define H2I_DMA_PORT_EN 0x9104 +#define H2I_DMA_PORT_EN_SY_IN 0x01 /* Synth_in DMA port */ +#define H2I_DMA_PORT_EN_AESRX 0x02 /* AES receiver DMA port */ +#define H2I_DMA_PORT_EN_AESTX 0x04 /* AES transmitter DMA port */ +#define H2I_DMA_PORT_EN_CODECTX 0x08 /* CODEC transmit DMA port */ +#define H2I_DMA_PORT_EN_CODECR 0x10 /* CODEC receive DMA port */ + +#define H2I_DMA_END 0x9108 /* global dma endian select */ +#define H2I_DMA_END_SY_IN 0x01 /* Synth_in DMA port */ +#define H2I_DMA_END_AESRX 0x02 /* AES receiver DMA port */ +#define H2I_DMA_END_AESTX 0x04 /* AES transmitter DMA port */ +#define H2I_DMA_END_CODECTX 0x08 /* CODEC transmit DMA port */ +#define H2I_DMA_END_CODECR 0x10 /* CODEC receive DMA port */ + /* 0=b_end 1=l_end */ + +#define H2I_DMA_DRV 0x910C /* global PBUS DMA enable */ + +#define H2I_SYNTH_C 0x1104 /* Synth DMA control */ + +#define H2I_AESRX_C 0x1204 /* AES RX dma control */ + +#define H2I_C_TS_EN 0x20 /* Timestamp enable */ +#define H2I_C_TS_FRMT 0x40 /* Timestamp format */ +#define H2I_C_NAUDIO 0x80 /* Sign extend */ + +/* AESRX CTL, 16 bit */ + +#define H2I_AESTX_C 0x1304 /* AES TX DMA control */ +#define H2I_AESTX_C_CLKID_SHIFT 3 /* Bresenham Clock Gen 1-3 */ +#define H2I_AESTX_C_CLKID_M 0x18 +#define H2I_AESTX_C_DATAT_SHIFT 8 /* 1=mono 2=stereo (3=quad) */ +#define H2I_AESTX_C_DATAT_M 0x300 + +/* CODEC registers */ + +#define H2I_DAC_C1 0x1404 /* DAC DMA control, 16 bit */ +#define H2I_DAC_C2 0x1408 /* DAC DMA control, 32 bit */ +#define H2I_ADC_C1 0x1504 /* ADC DMA control, 16 bit */ +#define H2I_ADC_C2 0x1508 /* ADC DMA control, 32 bit */ + +/* Bits in CTL1 register */ + +#define H2I_C1_DMA_SHIFT 0 /* DMA channel */ +#define H2I_C1_DMA_M 0x7 +#define H2I_C1_CLKID_SHIFT 3 /* Bresenham Clock Gen 1-3 */ +#define H2I_C1_CLKID_M 0x18 +#define H2I_C1_DATAT_SHIFT 8 /* 1=mono 2=stereo (3=quad) */ +#define H2I_C1_DATAT_M 0x300 + +/* Bits in CTL2 register */ + +#define H2I_C2_R_GAIN_SHIFT 0 /* right a/d input gain */ +#define H2I_C2_R_GAIN_M 0xf +#define H2I_C2_L_GAIN_SHIFT 4 /* left a/d input gain */ +#define H2I_C2_L_GAIN_M 0xf0 +#define H2I_C2_R_SEL 0x100 /* right input select */ +#define H2I_C2_L_SEL 0x200 /* left input select */ +#define H2I_C2_MUTE 0x400 /* mute */ +#define H2I_C2_DO1 0x00010000 /* digital output port bit 0 */ +#define H2I_C2_DO2 0x00020000 /* digital output port bit 1 */ +#define H2I_C2_R_ATT_SHIFT 18 /* right d/a output - */ +#define H2I_C2_R_ATT_M 0x007c0000 /* attenuation */ +#define H2I_C2_L_ATT_SHIFT 23 /* left d/a output - */ +#define H2I_C2_L_ATT_M 0x0f800000 /* attenuation */ + +#define H2I_SYNTH_MAP_C 0x1104 /* synth dma handshake ctrl */ + +/* Clock generator CTL 1, 16 bit */ + +#define H2I_BRES1_C1 0x2104 +#define H2I_BRES2_C1 0x2204 +#define H2I_BRES3_C1 0x2304 + +#define H2I_BRES_C1_SHIFT 0 /* 0=48.0 1=44.1 2=aes_rx */ +#define H2I_BRES_C1_M 0x03 + +/* Clock generator CTL 2, 32 bit */ + +#define H2I_BRES1_C2 0x2108 +#define H2I_BRES2_C2 0x2208 +#define H2I_BRES3_C2 0x2308 + +#define H2I_BRES_C2_INC_SHIFT 0 /* increment value */ +#define H2I_BRES_C2_INC_M 0xffff +#define H2I_BRES_C2_MOD_SHIFT 16 /* modcontrol value */ +#define H2I_BRES_C2_MOD_M 0xffff0000 /* modctrl=0xffff&(modinc-1) */ + +/* Unix timer, 64 bit */ + +#define H2I_UTIME 0x3104 +#define H2I_UTIME_0_LD 0xffff /* microseconds, LSB's */ +#define H2I_UTIME_1_LD0 0x0f /* microseconds, MSB's */ +#define H2I_UTIME_1_LD1 0xf0 /* tenths of microseconds */ +#define H2I_UTIME_2_LD 0xffff /* seconds, LSB's */ +#define H2I_UTIME_3_LD 0xffff /* seconds, MSB's */ + +typedef volatile u32 hal2_reg_t; + +typedef struct stru_hal2_ctl_regs hal2_ctl_regs_t; +struct stru_hal2_ctl_regs { + hal2_reg_t _unused0[4]; + hal2_reg_t isr; /* 0x10 Status Register */ + hal2_reg_t _unused1[3]; + hal2_reg_t rev; /* 0x20 Revision Register */ + hal2_reg_t _unused2[3]; + hal2_reg_t iar; /* 0x30 Indirect Address Register */ + hal2_reg_t _unused3[3]; + hal2_reg_t idr0; /* 0x40 Indirect Data Register 0 */ + hal2_reg_t _unused4[3]; + hal2_reg_t idr1; /* 0x50 Indirect Data Register 1 */ + hal2_reg_t _unused5[3]; + hal2_reg_t idr2; /* 0x60 Indirect Data Register 2 */ + hal2_reg_t _unused6[3]; + hal2_reg_t idr3; /* 0x70 Indirect Data Register 3 */ +}; + +typedef struct stru_hal2_aes_regs hal2_aes_regs_t; +struct stru_hal2_aes_regs { + hal2_reg_t rx_stat[2]; /* Status registers */ + hal2_reg_t rx_cr[2]; /* Control registers */ + hal2_reg_t rx_ud[4]; /* User data window */ + hal2_reg_t rx_st[24]; /* Channel status data */ + + hal2_reg_t tx_stat[1]; /* Status register */ + hal2_reg_t tx_cr[3]; /* Control registers */ + hal2_reg_t tx_ud[4]; /* User data window */ + hal2_reg_t tx_st[24]; /* Channel status data */ +}; + +typedef struct stru_hal2_vol_regs hal2_vol_regs_t; +struct stru_hal2_vol_regs { + hal2_reg_t right; /* 0x00 Right volume */ + hal2_reg_t left; /* 0x04 Left volume */ +}; + +typedef struct stru_hal2_syn_regs hal2_syn_regs_t; +struct stru_hal2_syn_regs { + hal2_reg_t _unused0[2]; + hal2_reg_t page; /* DOC Page register */ + hal2_reg_t regsel; /* DOC Register selection */ + hal2_reg_t dlow; /* DOC Data low */ + hal2_reg_t dhigh; /* DOC Data high */ + hal2_reg_t irq; /* IRQ Status */ + hal2_reg_t dram; /* DRAM Access */ +}; + +/* HAL2 specific structures */ + +typedef struct stru_hal2_pbus hal2_pbus_t; +struct stru_hal2_pbus { + struct hpc3_pbus_dmacregs *pbus; + int pbusnr; + unsigned int ctrl; /* Current state of pbus->pbdma_ctrl */ +}; + +typedef struct stru_hal2_binfo hal2_binfo_t; +typedef struct stru_hal2_buffer hal2_buf_t; +struct stru_hal2_binfo { + volatile struct hpc_dma_desc desc; + hal2_buf_t *next; /* pointer to next buffer */ + int cnt; /* bytes in buffer */ +}; +#define H2_BUFFER_SIZE (PAGE_SIZE - \ + ((sizeof(hal2_binfo_t) - 1) / 8 + 1) * 8) +struct stru_hal2_buffer { + hal2_binfo_t info; + char data[H2_BUFFER_SIZE] __attribute__((aligned(8))); +}; + +typedef struct stru_hal2_codec hal2_codec_t; +struct stru_hal2_codec { + hal2_buf_t *head; + hal2_buf_t *tail; + hal2_pbus_t pbus; + unsigned int format; /* Audio data format */ + int voices; /* mono/stereo */ + unsigned int sample_rate; + unsigned int master; /* Master frequency */ + unsigned short mod; /* MOD value */ + unsigned short inc; /* INC value */ + + wait_queue_head_t dma_wait; + spinlock_t lock; + struct semaphore sem; + + int usecount; /* recording and playback are + * independent */ +}; + +#define H2_MIX_OUTPUT_ATT 0 +#define H2_MIX_INPUT_GAIN 1 +#define H2_MIXERS 2 +typedef struct stru_hal2_mixer hal2_mixer_t; +struct stru_hal2_mixer { + int modcnt; + unsigned int volume[H2_MIXERS]; +}; + +typedef struct stru_hal2_card hal2_card_t; +struct stru_hal2_card { + int dev_dsp; /* audio device */ + int dev_mixer; /* mixer device */ + int dev_midi; /* midi device */ + + hal2_ctl_regs_t *ctl_regs; /* HAL2 ctl registers */ + hal2_aes_regs_t *aes_regs; /* HAL2 vol registers */ + hal2_vol_regs_t *vol_regs; /* HAL2 aes registers */ + hal2_syn_regs_t *syn_regs; /* HAL2 syn registers */ + + hal2_codec_t dac; + hal2_codec_t adc; + hal2_mixer_t mixer; +}; + +#endif /* __HAL2_H */ diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/harmony.c linux-2.6.0-test1-ac1/sound/oss/harmony.c --- linux-2.6.0-test1/sound/oss/harmony.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/harmony.c 2003-07-11 16:42:40.000000000 +0100 @@ -0,0 +1,1296 @@ +/* + drivers/sound/harmony.c + + This is a sound driver for ASP's and Lasi's Harmony sound chip + and is unlikely to be used for anything other than on a HP PA-RISC. + + Harmony is found in HP 712s, 715/new and many other GSC based machines. + On older 715 machines you'll find the technically identical chip + called 'Vivace'. Both Harmony and Vicace are supported by this driver. + + Copyright 2000 (c) Linuxcare Canada, Alex deVries + Copyright 2000-2002 (c) Helge Deller + Copyright 2001 (c) Matthieu Delahaye + Copyright 2001 (c) Jean-Christophe Vaugeois + + +TODO: + - fix SNDCTL_DSP_GETOSPACE and SNDCTL_DSP_GETISPACE ioctls to + return the real values + - add private ioctl for selecting line- or microphone input + (only one of them is available at the same time) + - add module parameters + - implement mmap functionality + - implement gain meter ? + - ... +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sound_config.h" + + +#define PFX "harmony: " +#define HARMONY_VERSION "V0.9a" + +#undef DEBUG +#ifdef DEBUG +# define DPRINTK printk +#else +# define DPRINTK(x,...) +#endif + + +#define MAX_BUFS 10 /* maximum number of rotating buffers */ +#define HARMONY_BUF_SIZE 4096 /* needs to be a multiple of PAGE_SIZE (4096)! */ + +#define CNTL_C 0x80000000 +#define CNTL_ST 0x00000020 +#define CNTL_44100 0x00000015 /* HARMONY_SR_44KHZ */ +#define CNTL_8000 0x00000008 /* HARMONY_SR_8KHZ */ + +#define GAINCTL_HE 0x08000000 +#define GAINCTL_LE 0x04000000 +#define GAINCTL_SE 0x02000000 + +#define DSTATUS_PN 0x00000200 +#define DSTATUS_RN 0x00000002 + +#define DSTATUS_IE 0x80000000 + +#define HARMONY_DF_16BIT_LINEAR 0 +#define HARMONY_DF_8BIT_ULAW 1 +#define HARMONY_DF_8BIT_ALAW 2 + +#define HARMONY_SS_MONO 0 +#define HARMONY_SS_STEREO 1 + +#define HARMONY_SR_8KHZ 0x08 +#define HARMONY_SR_16KHZ 0x09 +#define HARMONY_SR_27KHZ 0x0A +#define HARMONY_SR_32KHZ 0x0B +#define HARMONY_SR_48KHZ 0x0E +#define HARMONY_SR_9KHZ 0x0F +#define HARMONY_SR_5KHZ 0x10 +#define HARMONY_SR_11KHZ 0x11 +#define HARMONY_SR_18KHZ 0x12 +#define HARMONY_SR_22KHZ 0x13 +#define HARMONY_SR_37KHZ 0x14 +#define HARMONY_SR_44KHZ 0x15 +#define HARMONY_SR_33KHZ 0x16 +#define HARMONY_SR_6KHZ 0x17 + +/* + * Some magics numbers used to auto-detect file formats + */ + +#define HARMONY_MAGIC_8B_ULAW 1 +#define HARMONY_MAGIC_8B_ALAW 27 +#define HARMONY_MAGIC_16B_LINEAR 3 +#define HARMONY_MAGIC_MONO 1 +#define HARMONY_MAGIC_STEREO 2 + +/* + * Channels Positions in mixer register + */ + +#define GAIN_HE_SHIFT 27 +#define GAIN_HE_MASK ( 1 << GAIN_HE_SHIFT) +#define GAIN_LE_SHIFT 26 +#define GAIN_LE_MASK ( 1 << GAIN_LE_SHIFT) +#define GAIN_SE_SHIFT 25 +#define GAIN_SE_MASK ( 1 << GAIN_SE_SHIFT) +#define GAIN_IS_SHIFT 24 +#define GAIN_IS_MASK ( 1 << GAIN_IS_SHIFT) +#define GAIN_MA_SHIFT 20 +#define GAIN_MA_MASK ( 0x0f << GAIN_MA_SHIFT) +#define GAIN_LI_SHIFT 16 +#define GAIN_LI_MASK ( 0x0f << GAIN_LI_SHIFT) +#define GAIN_RI_SHIFT 12 +#define GAIN_RI_MASK ( 0x0f << GAIN_RI_SHIFT) +#define GAIN_LO_SHIFT 6 +#define GAIN_LO_MASK ( 0x3f << GAIN_LO_SHIFT) +#define GAIN_RO_SHIFT 0 +#define GAIN_RO_MASK ( 0x3f << GAIN_RO_SHIFT) + + +#define MAX_OUTPUT_LEVEL (GAIN_RO_MASK >> GAIN_RO_SHIFT) +#define MAX_INPUT_LEVEL (GAIN_RI_MASK >> GAIN_RI_SHIFT) +#define MAX_VOLUME_LEVEL (GAIN_MA_MASK >> GAIN_MA_SHIFT) + +/* + * Channels Mask in mixer register + */ + +#define GAIN_TOTAL_SILENCE 0x00F00FFF +#define GAIN_DEFAULT 0x0FF00000 + + +struct harmony_hpa { + u8 unused000; + u8 id; + u8 teleshare_id; + u8 unused003; + u32 reset; + u32 cntl; + u32 gainctl; + u32 pnxtadd; + u32 pcuradd; + u32 rnxtadd; + u32 rcuradd; + u32 dstatus; + u32 ov; + u32 pio; + u32 unused02c; + u32 unused030[3]; + u32 diag; +}; + +struct harmony_dev { + int irq; + struct harmony_hpa *hpa; + u32 current_gain; + u8 data_format; /* HARMONY_DF_xx_BIT_xxx */ + u8 sample_rate; /* HARMONY_SR_xx_KHZ */ + u8 stereo_select; /* HARMONY_SS_MONO or HARMONY_SS_STEREO */ + int format_initialized; + u32 dac_rate; /* 8000 ... 48000 (Hz) */ + int suspended_playing; + int suspended_recording; + + int blocked_playing; + int blocked_recording; + + wait_queue_head_t wq_play, wq_record; + int first_filled_play; /* first buffer containing data (next to play) */ + int nb_filled_play; + int play_offset; + int first_filled_record; + int nb_filled_record; + + int audio_open, mixer_open; + int dsp_unit, mixer_unit; + + struct pci_dev *fake_pci_dev; /* The fake pci_dev needed for + pci_* functions under ccio. */ +}; + + +static struct harmony_dev harmony; + + +/* + * Dynamic sound buffer allocation and DMA memory + */ + +struct harmony_buffer { + unsigned char *addr; + dma_addr_t dma_handle; + int dma_consistent; /* Zero if pci_alloc_consistent() fails */ + int len; +}; + +/* + * Harmony memory buffers + */ + +static struct harmony_buffer played_buf, recorded_buf, silent, graveyard; + + +#define CHECK_WBACK_INV_OFFSET(b,offset,len) \ + do { if (!b.dma_consistent) \ + dma_cache_wback_inv((unsigned long)b.addr+offset,len); \ + } while (0) + + +static int __init harmony_alloc_buffer(struct harmony_buffer *b, + int buffer_count) +{ + b->len = buffer_count * HARMONY_BUF_SIZE; + b->addr = pci_alloc_consistent(harmony.fake_pci_dev, + b->len, &b->dma_handle); + if (b->addr && b->dma_handle) { + b->dma_consistent = 1; + DPRINTK(KERN_INFO PFX "consistent memory: 0x%lx, played_buf: 0x%lx\n", + (unsigned long)b->dma_handle, (unsigned long)b->addr); + } else { + b->dma_consistent = 0; + /* kmalloc()ed memory will HPMC on ccio machines ! */ + b->addr = kmalloc(b->len, GFP_KERNEL); + if (!b->addr) { + printk(KERN_ERR PFX "couldn't allocate memory\n"); + return -EBUSY; + } + b->dma_handle = __pa(b->addr); + } + return 0; +} + +static void __exit harmony_free_buffer(struct harmony_buffer *b) +{ + if (!b->addr) + return; + + if (b->dma_consistent) + pci_free_consistent(harmony.fake_pci_dev, + b->len, b->addr, b->dma_handle); + else + kfree(b->addr); + + memset(b, 0, sizeof(*b)); +} + + + +/* + * Low-Level sound-chip programming + */ + +static void __inline__ harmony_wait_CNTL(void) +{ + /* Wait until we're out of control mode */ + while (gsc_readl(&harmony.hpa->cntl) & CNTL_C) + /* wait */ ; +} + + +static void harmony_update_control(void) +{ + u32 default_cntl; + + /* Set CNTL */ + default_cntl = (CNTL_C | /* The C bit */ + (harmony.data_format << 6) | /* Set the data format */ + (harmony.stereo_select << 5) | /* Stereo select */ + (harmony.sample_rate)); /* Set sample rate */ + harmony.format_initialized = 1; + + /* initialize CNTL */ + gsc_writel(default_cntl, &harmony.hpa->cntl); +} + +static void harmony_set_control(u8 data_format, u8 sample_rate, u8 stereo_select) +{ + harmony.sample_rate = sample_rate; + harmony.data_format = data_format; + harmony.stereo_select = stereo_select; + harmony_update_control(); +} + +static void harmony_set_rate(u8 data_rate) +{ + harmony.sample_rate = data_rate; + harmony_update_control(); +} + +static int harmony_detect_rate(int *freq) +{ + int newrate; + switch (*freq) { + case 8000: newrate = HARMONY_SR_8KHZ; break; + case 16000: newrate = HARMONY_SR_16KHZ; break; + case 27428: newrate = HARMONY_SR_27KHZ; break; + case 32000: newrate = HARMONY_SR_32KHZ; break; + case 48000: newrate = HARMONY_SR_48KHZ; break; + case 9600: newrate = HARMONY_SR_9KHZ; break; + case 5125: newrate = HARMONY_SR_5KHZ; break; + case 11025: newrate = HARMONY_SR_11KHZ; break; + case 18900: newrate = HARMONY_SR_18KHZ; break; + case 22050: newrate = HARMONY_SR_22KHZ; break; + case 37800: newrate = HARMONY_SR_37KHZ; break; + case 44100: newrate = HARMONY_SR_44KHZ; break; + case 33075: newrate = HARMONY_SR_33KHZ; break; + case 6615: newrate = HARMONY_SR_6KHZ; break; + default: newrate = HARMONY_SR_8KHZ; + *freq = 8000; break; + } + return newrate; +} + +static void harmony_set_format(u8 data_format) +{ + harmony.data_format = data_format; + harmony_update_control(); +} + +static void harmony_set_stereo(u8 stereo_select) +{ + harmony.stereo_select = stereo_select; + harmony_update_control(); +} + +static void harmony_disable_interrupts(void) +{ + harmony_wait_CNTL(); + gsc_writel(0, &harmony.hpa->dstatus); +} + +static void harmony_enable_interrupts(void) +{ + harmony_wait_CNTL(); + gsc_writel(DSTATUS_IE, &harmony.hpa->dstatus); +} + +/* + * harmony_silence() + * + * This subroutine fills in a buffer starting at location start and + * silences for length bytes. This references the current + * configuration of the audio format. + * + */ + +static void harmony_silence(struct harmony_buffer *buffer, int start, int length) +{ + u8 silence_char; + + /* Despite what you hear, silence is different in + different audio formats. */ + switch (harmony.data_format) { + case HARMONY_DF_8BIT_ULAW: silence_char = 0x55; break; + case HARMONY_DF_8BIT_ALAW: silence_char = 0xff; break; + case HARMONY_DF_16BIT_LINEAR: /* fall through */ + default: silence_char = 0; + } + + memset(buffer->addr+start, silence_char, length); +} + + +static int harmony_audio_open(struct inode *inode, struct file *file) +{ + if (harmony.audio_open) + return -EBUSY; + + harmony.audio_open++; + harmony.suspended_playing = harmony.suspended_recording = 1; + harmony.blocked_playing = harmony.blocked_recording = 0; + harmony.first_filled_play = harmony.first_filled_record = 0; + harmony.nb_filled_play = harmony.nb_filled_record = 0; + harmony.play_offset = 0; + init_waitqueue_head(&harmony.wq_play); + init_waitqueue_head(&harmony.wq_record); + + /* Start off in a balanced mode. */ + harmony_set_control(HARMONY_DF_8BIT_ULAW, HARMONY_SR_8KHZ, HARMONY_SS_MONO); + harmony_update_control(); + harmony.format_initialized = 0; + + /* Clear out all the buffers and flush to cache */ + harmony_silence(&played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS); + CHECK_WBACK_INV_OFFSET(played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS); + + return 0; +} + +/* + * Release (close) the audio device. + */ + +static int harmony_audio_release(struct inode *inode, struct file *file) +{ + if (!harmony.audio_open) + return -EBUSY; + + harmony.audio_open--; + + return 0; +} + +/* + * Read recorded data off the audio device. + */ + +static ssize_t harmony_audio_read(struct file *file, + char *buffer, + size_t size_count, + loff_t *ppos) +{ + int total_count = (int) size_count; + int count = 0; + int buf_to_read; + + while (count24) { + copy_from_user(file_header, buffer, sizeof(file_header)); + start_string = four_bytes_to_u32(0); + + if ((file_header[4]==0) && (start_string==0x2E736E64)) { + u32 format; + u32 nb_voices; + u32 speed; + + format = four_bytes_to_u32(12); + nb_voices = four_bytes_to_u32(20); + speed = four_bytes_to_u32(16); + + switch (format) { + case HARMONY_MAGIC_8B_ULAW: + harmony.data_format = HARMONY_DF_8BIT_ULAW; + break; + case HARMONY_MAGIC_8B_ALAW: + harmony.data_format = HARMONY_DF_8BIT_ALAW; + break; + case HARMONY_MAGIC_16B_LINEAR: + harmony.data_format = HARMONY_DF_16BIT_LINEAR; + break; + default: + harmony_set_control(HARMONY_DF_16BIT_LINEAR, + HARMONY_SR_44KHZ, HARMONY_SS_STEREO); + return; + } + switch (nb_voices) { + case HARMONY_MAGIC_MONO: + harmony.stereo_select = HARMONY_SS_MONO; + break; + case HARMONY_MAGIC_STEREO: + harmony.stereo_select = HARMONY_SS_STEREO; + break; + default: + harmony.stereo_select = HARMONY_SS_MONO; + break; + } + harmony_set_rate(harmony_detect_rate(&speed)); + harmony.dac_rate = speed; + return; + } + } + harmony_set_control(HARMONY_DF_8BIT_ULAW, HARMONY_SR_8KHZ, HARMONY_SS_MONO); +} +#undef four_bytes_to_u32 + + +static ssize_t harmony_audio_write(struct file *file, + const char *buffer, + size_t size_count, + loff_t *ppos) +{ + int total_count = (int) size_count; + int count = 0; + int frame_size; + int buf_to_fill; + + if (!harmony.format_initialized) + harmony_format_auto_detect(buffer, total_count); + + while (count= MAX_BUFS && !harmony.play_offset) { + harmony.blocked_playing = 1; + interruptible_sleep_on(&harmony.wq_play); + harmony.blocked_playing = 0; + } + if (harmony.nb_filled_play+2 >= MAX_BUFS && !harmony.play_offset) + return -EBUSY; + + + buf_to_fill = (harmony.first_filled_play+harmony.nb_filled_play); + if (harmony.play_offset) + buf_to_fill--; + buf_to_fill %= MAX_BUFS; + + /* Figure out the size of the frame */ + if ((total_count-count) > HARMONY_BUF_SIZE - harmony.play_offset) { + frame_size = HARMONY_BUF_SIZE - harmony.play_offset; + } else { + frame_size = total_count - count; + /* Clear out the buffer, since there we'll only be + overlaying part of the old buffer with the new one */ + harmony_silence(&played_buf, + HARMONY_BUF_SIZE*buf_to_fill+frame_size+harmony.play_offset, + HARMONY_BUF_SIZE-frame_size-harmony.play_offset); + } + + /* Copy the page to an aligned buffer */ + copy_from_user(played_buf.addr + (HARMONY_BUF_SIZE*buf_to_fill) + harmony.play_offset, + buffer+count, frame_size); + CHECK_WBACK_INV_OFFSET(played_buf, (HARMONY_BUF_SIZE*buf_to_fill + harmony.play_offset), + frame_size); + + if (!harmony.play_offset) + harmony.nb_filled_play++; + + count += frame_size; + harmony.play_offset += frame_size; + harmony.play_offset %= HARMONY_BUF_SIZE; + if (harmony.suspended_playing && (harmony.nb_filled_play>=4)) + harmony_enable_interrupts(); + } + + return count; +} + +static unsigned int harmony_audio_poll(struct file *file, + struct poll_table_struct *wait) +{ + unsigned int mask = 0; + + if (file->f_mode & FMODE_READ) { + if (!harmony.suspended_recording) + poll_wait(file, &harmony.wq_record, wait); + if (harmony.nb_filled_record) + mask |= POLLIN | POLLRDNORM; + } + + if (file->f_mode & FMODE_WRITE) { + if (!harmony.suspended_playing) + poll_wait(file, &harmony.wq_play, wait); + if (harmony.nb_filled_play) + mask |= POLLOUT | POLLWRNORM; + } + + return mask; +} + +static int harmony_audio_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + int ival, new_format; + int frag_size, frag_buf; + struct audio_buf_info info; + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *) arg); + + case SNDCTL_DSP_GETCAPS: + ival = DSP_CAP_DUPLEX; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETFMTS: + ival = (AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW ); + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SETFMT: + if (get_user(ival, (int *) arg)) + return -EFAULT; + if (ival != AFMT_QUERY) { + switch (ival) { + case AFMT_MU_LAW: new_format = HARMONY_DF_8BIT_ULAW; break; + case AFMT_A_LAW: new_format = HARMONY_DF_8BIT_ALAW; break; + case AFMT_S16_LE: /* fall through, but not really supported */ + case AFMT_S16_BE: new_format = HARMONY_DF_16BIT_LINEAR; + ival = AFMT_S16_BE; + break; + default: { + DPRINTK(KERN_WARNING PFX + "unsupported sound format 0x%04x requested.\n", + ival); + return -EINVAL; + } + } + harmony_set_format(new_format); + } else { + switch (harmony.data_format) { + case HARMONY_DF_8BIT_ULAW: ival = AFMT_MU_LAW; break; + case HARMONY_DF_8BIT_ALAW: ival = AFMT_A_LAW; break; + case HARMONY_DF_16BIT_LINEAR: ival = AFMT_U16_BE; break; + default: ival = 0; + } + } + return put_user(ival, (int *) arg); + + case SOUND_PCM_READ_RATE: + ival = harmony.dac_rate; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SPEED: + if (get_user(ival, (int *) arg)) + return -EFAULT; + harmony_set_rate(harmony_detect_rate(&ival)); + harmony.dac_rate = ival; + return put_user(ival, (int*) arg); + + case SNDCTL_DSP_STEREO: + if (get_user(ival, (int *) arg)) + return -EFAULT; + if (ival != 0 && ival != 1) + return -EINVAL; + harmony_set_stereo(ival); + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETBLKSIZE: + ival = HARMONY_BUF_SIZE; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_RESET: + if (!harmony.suspended_recording) { + /* TODO: stop_recording() */ + } + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(ival, (int *)arg)) + return -EFAULT; + frag_size = ival & 0xffff; + frag_buf = (ival>>16) & 0xffff; + /* TODO: We use hardcoded fragment sizes and numbers for now */ + frag_size = 12; /* 4096 == 2^12 */ + frag_buf = MAX_BUFS; + ival = (frag_buf << 16) + frag_size; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + info.fragstotal = MAX_BUFS; + info.fragments = MAX_BUFS - harmony.nb_filled_play; + info.fragsize = HARMONY_BUF_SIZE; + info.bytes = info.fragments * info.fragsize; + return copy_to_user((void *)arg, &info, sizeof(info)); + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + info.fragstotal = MAX_BUFS; + info.fragments = /*MAX_BUFS-*/ harmony.nb_filled_record; + info.fragsize = HARMONY_BUF_SIZE; + info.bytes = info.fragments * info.fragsize; + return copy_to_user((void *)arg, &info, sizeof(info)); + + case SNDCTL_DSP_SYNC: + return 0; + } + + return -EINVAL; +} + + +/* + * harmony_interrupt() + * + * harmony interruption service routine + * + */ + +static irqreturn_t harmony_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + u32 dstatus; + struct harmony_hpa *hpa; + + /* Setup the hpa */ + hpa = ((struct harmony_dev *)dev)->hpa; + harmony_wait_CNTL(); + + /* Read dstatus and pcuradd (the current address) */ + dstatus = gsc_readl(&hpa->dstatus); + + /* Turn off interrupts */ + harmony_disable_interrupts(); + + /* Check if this is a request to get the next play buffer */ + if (dstatus & DSTATUS_PN) { + if (!harmony.nb_filled_play) { + harmony.suspended_playing = 1; + gsc_writel((unsigned long)silent.dma_handle, &hpa->pnxtadd); + + if (!harmony.suspended_recording) + harmony_enable_interrupts(); + } else { + harmony.suspended_playing = 0; + gsc_writel((unsigned long)played_buf.dma_handle + + (HARMONY_BUF_SIZE*harmony.first_filled_play), + &hpa->pnxtadd); + harmony.first_filled_play++; + harmony.first_filled_play %= MAX_BUFS; + harmony.nb_filled_play--; + + harmony_enable_interrupts(); + } + + if (harmony.blocked_playing) + wake_up_interruptible(&harmony.wq_play); + } + + /* Check if we're being asked to fill in a recording buffer */ + if (dstatus & DSTATUS_RN) { + if((harmony.nb_filled_record+2>=MAX_BUFS) || harmony.suspended_recording) + { + harmony.nb_filled_record = 0; + harmony.first_filled_record = 0; + harmony.suspended_recording = 1; + gsc_writel((unsigned long)graveyard.dma_handle, &hpa->rnxtadd); + if (!harmony.suspended_playing) + harmony_enable_interrupts(); + } else { + int buf_to_fill; + buf_to_fill = (harmony.first_filled_record+harmony.nb_filled_record) % MAX_BUFS; + CHECK_WBACK_INV_OFFSET(recorded_buf, HARMONY_BUF_SIZE*buf_to_fill, HARMONY_BUF_SIZE); + gsc_writel((unsigned long)recorded_buf.dma_handle + + HARMONY_BUF_SIZE*buf_to_fill, + &hpa->rnxtadd); + harmony.nb_filled_record++; + harmony_enable_interrupts(); + } + + if (harmony.blocked_recording && harmony.nb_filled_record>3) + wake_up_interruptible(&harmony.wq_record); + } + return IRQ_HANDLED; +} + +/* + * Sound playing functions + */ + +static struct file_operations harmony_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: harmony_audio_read, + write: harmony_audio_write, + poll: harmony_audio_poll, + ioctl: harmony_audio_ioctl, + open: harmony_audio_open, + release:harmony_audio_release, +}; + +static int harmony_audio_init(void) +{ + /* Request that IRQ */ + if (request_irq(harmony.irq, harmony_interrupt, 0 ,"harmony", &harmony)) { + printk(KERN_ERR PFX "Error requesting irq %d.\n", harmony.irq); + return -EFAULT; + } + + harmony.dsp_unit = register_sound_dsp(&harmony_audio_fops, -1); + if (harmony.dsp_unit < 0) { + printk(KERN_ERR PFX "Error registering dsp\n"); + free_irq(harmony.irq, &harmony); + return -EFAULT; + } + + /* Clear the buffers so you don't end up with crap in the buffers. */ + harmony_silence(&played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS); + + /* Make sure this makes it to cache */ + CHECK_WBACK_INV_OFFSET(played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS); + + /* Clear out the silent buffer and flush to cache */ + harmony_silence(&silent, 0, HARMONY_BUF_SIZE); + CHECK_WBACK_INV_OFFSET(silent, 0, HARMONY_BUF_SIZE); + + harmony.audio_open = 0; + + return 0; +} + + +/* + * mixer functions + */ + +static void harmony_mixer_set_gain(void) +{ + harmony_wait_CNTL(); + gsc_writel(harmony.current_gain, &harmony.hpa->gainctl); +} + +/* + * Read gain of selected channel. + * The OSS rate is from 0 (silent) to 100 -> need some conversions + * + * The harmony gain are attenuation for output and monitor gain. + * is amplifaction for input gain + */ +#define to_harmony_level(level,max) ((level)*max/100) +#define to_oss_level(level,max) ((level)*100/max) + +static int harmony_mixer_get_level(int channel) +{ + int left_level; + int right_level; + + switch (channel) { + case SOUND_MIXER_OGAIN: + left_level = (harmony.current_gain & GAIN_LO_MASK) >> GAIN_LO_SHIFT; + right_level = (harmony.current_gain & GAIN_RO_MASK) >> GAIN_RO_SHIFT; + left_level = to_oss_level(MAX_OUTPUT_LEVEL - left_level, MAX_OUTPUT_LEVEL); + right_level = to_oss_level(MAX_OUTPUT_LEVEL - right_level, MAX_OUTPUT_LEVEL); + return (right_level << 8)+left_level; + + case SOUND_MIXER_IGAIN: + left_level = (harmony.current_gain & GAIN_LI_MASK) >> GAIN_LI_SHIFT; + right_level= (harmony.current_gain & GAIN_RI_MASK) >> GAIN_RI_SHIFT; + left_level = to_oss_level(left_level, MAX_INPUT_LEVEL); + right_level= to_oss_level(right_level, MAX_INPUT_LEVEL); + return (right_level << 8)+left_level; + + case SOUND_MIXER_VOLUME: + left_level = (harmony.current_gain & GAIN_MA_MASK) >> GAIN_MA_SHIFT; + left_level = to_oss_level(MAX_VOLUME_LEVEL-left_level, MAX_VOLUME_LEVEL); + return left_level; + } + return -EINVAL; +} + + + +/* + * Some conversions for the same reasons. + * We give back the new real value(s) due to + * the rescale. + */ + +static int harmony_mixer_set_level(int channel, int value) +{ + int left_level; + int right_level; + int new_left_level; + int new_right_level; + + right_level = (value & 0x0000ff00) >> 8; + left_level = value & 0x000000ff; + + switch (channel) { + case SOUND_MIXER_OGAIN: + right_level = to_harmony_level(100-right_level, MAX_OUTPUT_LEVEL); + left_level = to_harmony_level(100-left_level, MAX_OUTPUT_LEVEL); + new_right_level = to_oss_level(MAX_OUTPUT_LEVEL - right_level, MAX_OUTPUT_LEVEL); + new_left_level = to_oss_level(MAX_OUTPUT_LEVEL - left_level, MAX_OUTPUT_LEVEL); + harmony.current_gain = (harmony.current_gain & ~(GAIN_LO_MASK | GAIN_RO_MASK)) + | (left_level << GAIN_LO_SHIFT) | (right_level << GAIN_RO_SHIFT); + harmony_mixer_set_gain(); + return (new_right_level << 8) + new_left_level; + + case SOUND_MIXER_IGAIN: + right_level = to_harmony_level(right_level, MAX_INPUT_LEVEL); + left_level = to_harmony_level(left_level, MAX_INPUT_LEVEL); + new_right_level = to_oss_level(right_level, MAX_INPUT_LEVEL); + new_left_level = to_oss_level(left_level, MAX_INPUT_LEVEL); + harmony.current_gain = (harmony.current_gain & ~(GAIN_LI_MASK | GAIN_RI_MASK)) + | (left_level << GAIN_LI_SHIFT) | (right_level << GAIN_RI_SHIFT); + harmony_mixer_set_gain(); + return (new_right_level << 8) + new_left_level; + + case SOUND_MIXER_VOLUME: + left_level = to_harmony_level(100-left_level, MAX_VOLUME_LEVEL); + new_left_level = to_oss_level(MAX_VOLUME_LEVEL-left_level, MAX_VOLUME_LEVEL); + harmony.current_gain = (harmony.current_gain & ~GAIN_MA_MASK)| (left_level << GAIN_MA_SHIFT); + harmony_mixer_set_gain(); + return new_left_level; + } + + return -EINVAL; +} + +#undef to_harmony_level +#undef to_oss_level + +/* + * Return the selected input device (mic or line) + */ + +static int harmony_mixer_get_recmask(void) +{ + int current_input_line; + + current_input_line = (harmony.current_gain & GAIN_IS_MASK) + >> GAIN_IS_SHIFT; + if (current_input_line) + return SOUND_MASK_MIC; + + return SOUND_MASK_LINE; +} + +/* + * Set the input (only one at time, arbitrary priority to line in) + */ + +static int harmony_mixer_set_recmask(int recmask) +{ + int new_input_line; + int new_input_mask; + + if ((recmask & SOUND_MASK_LINE)) { + new_input_line = 0; + new_input_mask = SOUND_MASK_LINE; + } else { + new_input_line = 1; + new_input_mask = SOUND_MASK_MIC; + } + harmony.current_gain = ((harmony.current_gain & ~GAIN_IS_MASK) | + (new_input_line << GAIN_IS_SHIFT )); + harmony_mixer_set_gain(); + return new_input_mask; +} + + +/* + * give the active outlines + */ + +static int harmony_mixer_get_outmask(void) +{ + int outmask = 0; + + if (harmony.current_gain & GAIN_HE_MASK) outmask |=SOUND_MASK_PHONEOUT; + if (harmony.current_gain & GAIN_LE_MASK) outmask |=SOUND_MASK_LINE; + if (harmony.current_gain & GAIN_SE_MASK) outmask |=SOUND_MASK_SPEAKER; + + return outmask; +} + + +static int harmony_mixer_set_outmask(int outmask) +{ + if (outmask & SOUND_MASK_PHONEOUT) + harmony.current_gain |= GAIN_HE_MASK; + else + harmony.current_gain &= ~GAIN_HE_MASK; + + if (outmask & SOUND_MASK_LINE) + harmony.current_gain |= GAIN_LE_MASK; + else + harmony.current_gain &= ~GAIN_LE_MASK; + + if (outmask & SOUND_MASK_SPEAKER) + harmony.current_gain |= GAIN_SE_MASK; + else + harmony.current_gain &= ~GAIN_SE_MASK; + + harmony_mixer_set_gain(); + + return (outmask & (SOUND_MASK_PHONEOUT | SOUND_MASK_LINE | SOUND_MASK_SPEAKER)); +} + +/* + * This code is inspired from sb_mixer.c + */ + +static int harmony_mixer_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int val; + int ret; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + memset(&info, 0, sizeof(info)); + strncpy(info.id, "harmony", sizeof(info.id)-1); + strncpy(info.name, "Harmony audio", sizeof(info.name)-1); + info.modify_counter = 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 */ + val = 0; + 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 = SOUND_MASK_IGAIN | SOUND_MASK_OGAIN; + break; + + case MIXER_READ(SOUND_MIXER_RECMASK): + ret = SOUND_MASK_MIC | SOUND_MASK_LINE; + break; + case MIXER_READ(SOUND_MIXER_DEVMASK): + ret = SOUND_MASK_OGAIN | SOUND_MASK_IGAIN | + SOUND_MASK_VOLUME; + break; + case MIXER_READ(SOUND_MIXER_OUTMASK): + ret = SOUND_MASK_SPEAKER | SOUND_MASK_LINE | + SOUND_MASK_PHONEOUT; + break; + + case MIXER_WRITE(SOUND_MIXER_RECSRC): + ret = harmony_mixer_set_recmask(val); + break; + case MIXER_READ(SOUND_MIXER_RECSRC): + ret = harmony_mixer_get_recmask(); + break; + + case MIXER_WRITE(SOUND_MIXER_OUTSRC): + ret = harmony_mixer_set_outmask(val); + break; + case MIXER_READ(SOUND_MIXER_OUTSRC): + ret = harmony_mixer_get_outmask(); + break; + + case MIXER_WRITE(SOUND_MIXER_OGAIN): + case MIXER_WRITE(SOUND_MIXER_IGAIN): + case MIXER_WRITE(SOUND_MIXER_VOLUME): + ret = harmony_mixer_set_level(cmd & 0xff, val); + break; + + case MIXER_READ(SOUND_MIXER_OGAIN): + case MIXER_READ(SOUND_MIXER_IGAIN): + case MIXER_READ(SOUND_MIXER_VOLUME): + ret = harmony_mixer_get_level(cmd & 0xff); + break; + + default: + return -EINVAL; + } + + if (put_user(ret, (int *)arg)) + return -EFAULT; + return 0; +} + + +static int harmony_mixer_open(struct inode *inode, struct file *file) +{ + if (harmony.mixer_open) + return -EBUSY; + harmony.mixer_open++; + return 0; +} + +static int harmony_mixer_release(struct inode *inode, struct file *file) +{ + if (!harmony.mixer_open) + return -EBUSY; + harmony.mixer_open--; + return 0; +} + +static struct file_operations harmony_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + open: harmony_mixer_open, + release: harmony_mixer_release, + ioctl: harmony_mixer_ioctl, +}; + + +/* + * Mute all the output and reset Harmony. + */ + +static void __init harmony_mixer_reset(void) +{ + harmony.current_gain = GAIN_TOTAL_SILENCE; + harmony_mixer_set_gain(); + harmony_wait_CNTL(); + gsc_writel(1, &harmony.hpa->reset); + mdelay(50); /* wait 50 ms */ + gsc_writel(0, &harmony.hpa->reset); + harmony.current_gain = GAIN_DEFAULT; + harmony_mixer_set_gain(); +} + +static int __init harmony_mixer_init(void) +{ + /* Register the device file operations */ + harmony.mixer_unit = register_sound_mixer(&harmony_mixer_fops, -1); + if (harmony.mixer_unit < 0) { + printk(KERN_WARNING PFX "Error Registering Mixer Driver\n"); + return -EFAULT; + } + + harmony_mixer_reset(); + harmony.mixer_open = 0; + + return 0; +} + + + +/* + * This is the callback that's called by the inventory hardware code + * if it finds a match to the registered driver. + */ +static int __init +harmony_driver_callback(struct parisc_device *dev) +{ + u8 id; + u8 rev; + u32 cntl; + int ret; + + if (harmony.hpa) { + /* We only support one Harmony at this time */ + printk(KERN_ERR PFX "driver already registered\n"); + return -EBUSY; + } + + /* Set the HPA of harmony */ + harmony.hpa = (struct harmony_hpa *)dev->hpa; + + harmony.irq = dev->irq; + if (!harmony.irq) { + printk(KERN_ERR PFX "no irq found\n"); + return -ENODEV; + } + + /* Grab the ID and revision from the device */ + id = gsc_readb(&harmony.hpa->id); + if ((id | 1) != 0x15) { + printk(KERN_WARNING PFX "wrong harmony id 0x%02x\n", id); + return -EBUSY; + } + cntl = gsc_readl(&harmony.hpa->cntl); + rev = (cntl>>20) & 0xff; + + printk(KERN_INFO "Lasi Harmony Audio driver " HARMONY_VERSION ", " + "h/w id %i, rev. %i at 0x%lx, IRQ %i\n", + id, rev, dev->hpa, harmony.irq); + + /* Make sure the control bit isn't set, although I don't think it + ever is. */ + if (cntl & CNTL_C) { + printk(KERN_WARNING PFX "CNTL busy\n"); + harmony.hpa = 0; + return -EBUSY; + } + + /* a fake pci_dev is needed for pci_* functions under ccio */ + harmony.fake_pci_dev = ccio_get_fake(dev); + + /* Initialize the memory buffers */ + if (harmony_alloc_buffer(&played_buf, MAX_BUFS) || + harmony_alloc_buffer(&recorded_buf, MAX_BUFS) || + harmony_alloc_buffer(&graveyard, 1) || + harmony_alloc_buffer(&silent, 1)) { + ret = -EBUSY; + goto out_err; + } + + /* Initialize /dev/mixer and /dev/audio */ + if ((ret=harmony_mixer_init())) + goto out_err; + if ((ret=harmony_audio_init())) + goto out_err; + + return 0; + +out_err: + harmony.hpa = 0; + harmony_free_buffer(&played_buf); + harmony_free_buffer(&recorded_buf); + harmony_free_buffer(&graveyard); + harmony_free_buffer(&silent); + return ret; +} + + +static struct parisc_device_id harmony_tbl[] = { + /* { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A }, Bushmaster/Flounder */ + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007B }, /* 712/715 Audio */ + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007E }, /* Pace Audio */ + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007F }, /* Outfield / Coral II */ + { 0, } +}; + +MODULE_DEVICE_TABLE(parisc, harmony_tbl); + +static struct parisc_driver harmony_driver = { + name: "Lasi Harmony", + id_table: harmony_tbl, + probe: harmony_driver_callback, +}; + +static int __init init_harmony(void) +{ + return register_parisc_driver(&harmony_driver); +} + +static void __exit cleanup_harmony(void) +{ + free_irq(harmony.irq, &harmony); + unregister_sound_mixer(harmony.mixer_unit); + unregister_sound_dsp(harmony.dsp_unit); + harmony_free_buffer(&played_buf); + harmony_free_buffer(&recorded_buf); + harmony_free_buffer(&graveyard); + harmony_free_buffer(&silent); + unregister_parisc_driver(&harmony_driver); +} + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Alex DeVries "); +MODULE_DESCRIPTION("Harmony sound driver"); +MODULE_LICENSE("GPL"); + +module_init(init_harmony); +module_exit(cleanup_harmony); + diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/i810_audio.c linux-2.6.0-test1-ac1/sound/oss/i810_audio.c --- linux-2.6.0-test1/sound/oss/i810_audio.c 2003-07-14 14:11:57.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/i810_audio.c 2003-07-11 17:34:21.000000000 +0100 @@ -181,7 +181,7 @@ struct i810_channel { /* these sg guys should probably be allocated - separately as nocache. Must be 8 byte aligned */ + seperately as nocache. Must be 8 byte aligned */ struct sg_item sg[SG_LEN]; /* 32*8 */ u32 offset; /* 4 */ u32 port; /* 4 */ @@ -190,7 +190,7 @@ }; /* - * we have 3 separate dma engines. pcm in, pcm out, and mic. + * 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. @@ -879,7 +879,7 @@ #define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) #define DMABUF_MINORDER 1 -/* allocate DMA buffer, playback and recording buffer should be allocated separately */ +/* allocate DMA buffer, playback and recording buffer should be allocated seperately */ static int alloc_dmabuf(struct i810_state *state) { struct dmabuf *dmabuf = &state->dmabuf; diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/kahlua.c linux-2.6.0-test1-ac1/sound/oss/kahlua.c --- linux-2.6.0-test1/sound/oss/kahlua.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/kahlua.c 2003-07-14 14:39:10.000000000 +0100 @@ -0,0 +1,224 @@ +/* + * Initialisation code for Cyrix/NatSemi VSA1 softaudio + * + * (C) Copyright 2003 Red Hat Inc + * + * XpressAudio(tm) is used on the Cyrix MediaGX (now NatSemi Geode) systems. + * The older version (VSA1) provides fairly good soundblaster emulation + * although there are a couple of bugs: large DMA buffers break record, + * and the MPU event handling seems suspect. VSA2 allows the native driver + * to control the AC97 audio engine directly and requires a different driver. + * + * Thanks to National Semiconductor for providing the needed information + * on the XpressAudio(tm) internals. + * + * This program is free software; you can redistribute 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * TO DO: + * Investigate whether we can portably support Cognac (5520) in the + * same manner. + */ + +#include +#include +#include +#include + +#include "sound_config.h" + +#include "sb.h" + +/* + * Read a soundblaster compatible mixer register. + * In this case we are actually reading an SMI trap + * not real hardware. + */ + +static u8 __devinit mixer_read(unsigned long io, u8 reg) +{ + outb(reg, io + 4); + udelay(20); + reg = inb(io + 5); + udelay(20); + return reg; +} + +static int __devinit probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct address_info *hw_config; + unsigned long base; + void *mem; + unsigned long io; + u16 map; + u8 irq, dma8, dma16; + int oldquiet; + extern int sb_be_quiet; + + base = pci_resource_start(pdev, 0); + if(base == 0UL) + return 1; + + mem = ioremap(base, 128); + if(mem == 0UL) + return 1; + map = readw(mem + 0x18); /* Read the SMI enables */ + iounmap(mem); + + /* Map bits + 0:1 * 0x20 + 0x200 = sb base + 2 sb enable + 3 adlib enable + 5 MPU enable 0x330 + 6 MPU enable 0x300 + + The other bits may be used internally so must be masked */ + + io = 0x220 + 0x20 * (map & 3); + + if(map & (1<<2)) + printk(KERN_INFO "kahlua: XpressAudio at 0x%lx\n", io); + else + return 1; + + if(map & (1<<5)) + printk(KERN_INFO "kahlua: MPU at 0x300\n"); + else if(map & (1<<6)) + printk(KERN_INFO "kahlua: MPU at 0x330\n"); + + irq = mixer_read(io, 0x80) & 0x0F; + dma8 = mixer_read(io, 0x81); + + // printk("IRQ=%x MAP=%x DMA=%x\n", irq, map, dma8); + + if(dma8 & 0x20) + dma16 = 5; + else if(dma8 & 0x40) + dma16 = 6; + else if(dma8 & 0x80) + dma16 = 7; + else + { + printk(KERN_ERR "kahlua: No 16bit DMA enabled.\n"); + return 1; + } + + if(dma8 & 0x01) + dma8 = 0; + else if(dma8 & 0x02) + dma8 = 1; + else if(dma8 & 0x08) + dma8 = 3; + else + { + printk(KERN_ERR "kahlua: No 8bit DMA enabled.\n"); + return 1; + } + + if(irq & 1) + irq = 9; + else if(irq & 2) + irq = 5; + else if(irq & 4) + irq = 7; + else if(irq & 8) + irq = 10; + else + { + printk(KERN_ERR "kahlua: SB IRQ not set.\n"); + return 1; + } + + printk(KERN_INFO "kahlua: XpressAudio on IRQ %d, DMA %d, %d\n", + irq, dma8, dma16); + + hw_config = kmalloc(sizeof(struct address_info), GFP_KERNEL); + if(hw_config == NULL) + { + printk(KERN_ERR "kahlua: out of memory.\n"); + return 1; + } + memset(hw_config, 0, sizeof(*hw_config)); + + pci_set_drvdata(pdev, hw_config); + + hw_config->io_base = io; + hw_config->irq = irq; + hw_config->dma = dma8; + hw_config->dma2 = dma16; + hw_config->name = "Cyrix XpressAudio"; + hw_config->driver_use_1 = SB_NO_MIDI | SB_PCI_IRQ; + + if(sb_dsp_detect(hw_config, 0, 0, NULL)==0) + { + printk(KERN_ERR "kahlua: audio not responding.\n"); + return 1; + } + + oldquiet = sb_be_quiet; + sb_be_quiet = 1; + if(sb_dsp_init(hw_config, THIS_MODULE)) + { + sb_be_quiet = oldquiet; + pci_set_drvdata(pdev, NULL); + kfree(hw_config); + return 1; + } + sb_be_quiet = oldquiet; + + return 0; +} + +static void __devexit remove_one(struct pci_dev *pdev) +{ + struct address_info *hw_config = pci_get_drvdata(pdev); + sb_dsp_unload(hw_config, 0); + pci_set_drvdata(pdev, NULL); + kfree(hw_config); +} + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("Kahlua VSA1 PCI Audio"); +MODULE_LICENSE("GPL"); + +/* + * 5530 only. The 5510/5520 decode is different. + */ + +static struct pci_device_id id_tbl[] __devinitdata = { + { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(pci, id_tbl); + +static struct pci_driver kahlua_driver = { + name: "kahlua", + id_table: id_tbl, + probe: probe_one, + remove: __devexit_p(remove_one), +}; + + +static int __init kahlua_init_module(void) +{ + printk(KERN_INFO "Cyrix Kahlua VSA1 XpressAudio support (c) Copyright 2003 Red Hat Inc\n"); + return pci_module_init(&kahlua_driver); +} + +static void __devexit kahlua_cleanup_module(void) +{ + return pci_unregister_driver(&kahlua_driver); +} + + +module_init(kahlua_init_module); +module_exit(kahlua_cleanup_module); + diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/Kconfig linux-2.6.0-test1-ac1/sound/oss/Kconfig --- linux-2.6.0-test1/sound/oss/Kconfig 2003-07-14 14:11:57.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/Kconfig 2003-07-14 14:25:18.000000000 +0100 @@ -1126,3 +1126,23 @@ Support for audio mixer facilities on the BT848 TV frame-grabber card. +config SOUND_ALI5455 + tristate "ALi5455 audio support" + depends on SOUND_PRIME!=n && PCI + +config SOUND_FORTE + tristate "ForteMedia FM801 driver" + depends on SOUND_PRIME!=n && PCI + +config SOUND_RME96XX + tristate "RME Hammerfall (RME96XX) support" + depends on SOUND_PRIME!=n && PCI + +config SOUND_AD1980 + tristate "AD1980 front/back switch plugin" + depends on SOUND_PRIME!=n + +config SOUND_WM97XX + tristate "Wolfson Touchscreen/BMON plugin" + depends on SOUND_PRIME!=n + diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/Makefile linux-2.6.0-test1-ac1/sound/oss/Makefile --- linux-2.6.0-test1/sound/oss/Makefile 2003-07-14 14:11:57.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/Makefile 2003-07-14 14:25:22.000000000 +0100 @@ -63,7 +63,12 @@ obj-$(CONFIG_SOUND_EMU10K1) += ac97_codec.o obj-$(CONFIG_SOUND_RME96XX) += rme96xx.o obj-$(CONFIG_SOUND_BT878) += btaudio.o +obj-$(CONFIG_SOUND_ALI5455) += ali5455.o ac97_codec.o obj-$(CONFIG_SOUND_IT8172) += ite8172.o ac97_codec.o +obj-$(CONFIG_SOUND_FORTE) += forte.o + +obj-$(CONFIG_SOUND_AD1980) += ac97_plugin_ad1980.o +obj-$(CONFIG_SOUND_WM97XX) += ac97_plugin_wm97xx.o ifeq ($(CONFIG_MIDI_EMU10K1),y) obj-$(CONFIG_SOUND_EMU10K1) += sound.o diff -u --new-file --exclude-from /usr/src/exclude --recursive linux-2.6.0-test1/sound/oss/wm97xx.h linux-2.6.0-test1-ac1/sound/oss/wm97xx.h --- linux-2.6.0-test1/sound/oss/wm97xx.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.0-test1-ac1/sound/oss/wm97xx.h 2003-07-11 18:54:05.000000000 +0100 @@ -0,0 +1,96 @@ + +/* + * Register bits for Wolfson WM97xx series of codecs + */ + +#ifndef _WM97XX_H_ +#define _WM97XX_H_ + +#include /* AC97 control layer */ + +/* + * WM97xx AC97 Touchscreen registers + */ +#define AC97_WM97XX_DIGITISER1 0x76 +#define AC97_WM97XX_DIGITISER2 0x78 +#define AC97_WM97XX_DIGITISER_RD 0x7a + +/* + * WM97xx register bits + */ +#define WM97XX_POLL 0x8000 /* initiate a polling measurement */ +#define WM97XX_ADCSEL_X 0x1000 /* x coord measurement */ +#define WM97XX_ADCSEL_Y 0x2000 /* y coord measurement */ +#define WM97XX_ADCSEL_PRES 0x3000 /* pressure measurement */ +#define WM97XX_COO 0x0800 /* enable coordinate mode */ +#define WM97XX_CTC 0x0400 /* enable continuous mode */ +#define WM97XX_CM_RATE_93 0x0000 /* 93.75Hz continuous rate */ +#define WM97XX_CM_RATE_187 0x0100 /* 187.5Hz continuous rate */ +#define WM97XX_CM_RATE_375 0x0200 /* 375Hz continuous rate */ +#define WM97XX_CM_RATE_750 0x0300 /* 750Hz continuous rate */ +#define WM97XX_CM_RATE_8K 0x00f0 /* 8kHz continuous rate */ +#define WM97XX_CM_RATE_12K 0x01f0 /* 12kHz continuous rate */ +#define WM97XX_CM_RATE_24K 0x02f0 /* 24kHz continuous rate */ +#define WM97XX_CM_RATE_48K 0x03f0 /* 48kHz continuous rate */ +#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */ +#define WM97XX_SLEN 0x0008 /* slot read back enable */ +#define WM97XX_SLT(i) ((i - 5) & 0x7) /* touchpanel slot selection (5-11) */ +#define WM97XX_PRP_DETW 0x4000 /* pen detect on, digitiser off, wake up */ +#define WM97XX_PRP_DET 0x8000 /* pen detect on, digitiser off, no wake up */ +#define WM97XX_PRP_DET_DIG 0xc000 /* pen detect on, digitiser on */ +#define WM97XX_RPR 0x2000 /* wake up on pen down */ +#define WM97XX_PEN_DOWN 0x8000 /* pen is down */ + +/* WM9712 Bits */ +#define WM9712_45W 0x1000 /* set for 5-wire touchscreen */ +#define WM9712_PDEN 0x0800 /* measure only when pen down */ +#define WM9712_WAIT 0x0200 /* wait until adc is read before next sample */ +#define WM9712_PIL 0x0100 /* current used for pressure measurement. set 400uA else 200uA */ +#define WM9712_MASK_HI 0x0040 /* hi on mask pin (47) stops conversions */ +#define WM9712_MASK_EDGE 0x0080 /* rising/falling edge on pin delays sample */ +#define WM9712_MASK_SYNC 0x00c0 /* rising/falling edge on mask initiates sample */ +#define WM9712_RPU(i) (i&0x3f) /* internal pull up on pen detect (64k / rpu) */ +#define WM9712_ADCSEL_COMP1 0x4000 /* COMP1/AUX1 measurement (pin29) */ +#define WM9712_ADCSEL_COMP2 0x5000 /* COMP2/AUX2 measurement (pin30) */ +#define WM9712_ADCSEL_BMON 0x6000 /* BMON/AUX3 measurement (pin31) */ +#define WM9712_ADCSEL_WIPER 0x7000 /* WIPER/AUX4 measurement (pin12) */ +#define WM9712_PD(i) (0x1 << i) /* power management */ + +/* WM9712 Registers */ +#define AC97_WM9712_POWER 0x24 +#define AC97_WM9712_REV 0x58 + +/* WM9705 Bits */ +#define WM9705_PDEN 0x1000 /* measure only when pen is down */ +#define WM9705_PINV 0x0800 /* inverts sense of pen down output */ +#define WM9705_BSEN 0x0400 /* BUSY flag enable, pin47 is 1 when busy */ +#define WM9705_BINV 0x0200 /* invert BUSY (pin47) output */ +#define WM9705_WAIT 0x0100 /* wait until adc is read before next sample */ +#define WM9705_PIL 0x0080 /* current used for pressure measurement. set 400uA else 200uA */ +#define WM9705_PHIZ 0x0040 /* set PHONE and PCBEEP inputs to high impedance */ +#define WM9705_MASK_HI 0x0010 /* hi on mask stops conversions */ +#define WM9705_MASK_EDGE 0x0020 /* rising/falling edge on pin delays sample */ +#define WM9705_MASK_SYNC 0x0030 /* rising/falling edge on mask initiates sample */ +#define WM9705_PDD(i) (i & 0x000f) /* pen detect comparator threshold */ +#define WM9705_ADCSEL_BMON 0x4000 /* BMON measurement */ +#define WM9705_ADCSEL_AUX 0x5000 /* AUX measurement */ +#define WM9705_ADCSEL_PHONE 0x6000 /* PHONE measurement */ +#define WM9705_ADCSEL_PCBEEP 0x7000 /* PCBEEP measurement */ + +/* AUX ADC ID's */ +#define TS_COMP1 0x0 +#define TS_COMP2 0x1 +#define TS_BMON 0x2 +#define TS_WIPER 0x3 + +/* ID numbers */ +#define WM97XX_ID1 0x574d +#define WM9712_ID2 0x4c12 +#define WM9705_ID2 0x4c05 + +#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */ + +void register_touchscreen_codec(struct ac97_codec *codec); +void unregister_touchscreen_codec(struct ac97_codec *codec); + +#endif