From ef8ff89b58546055e238c3b521f83b440dfe8ef2 Mon Sep 17 00:00:00 2001 From: Michal Marek Date: Tue, 9 Mar 2010 16:00:20 +0100 Subject: kbuild: Really don't clean bounds.h and asm-offsets.h Commit 7d3cc8b tried to keep bounds.h and asm-offsets.h during make clean by filtering these out of $(clean-files), but they are listed in $(targets) and $(always) and thus removed automatically. Introduce a new $(no-clean-files) variable to really skip such files in Makefile.clean. Signed-off-by: Michal Marek --- Documentation/kbuild/makefiles.txt | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kbuild/makefiles.txt b/Documentation/kbuild/makefiles.txt index 01351554df60..fcd1a981523a 100644 --- a/Documentation/kbuild/makefiles.txt +++ b/Documentation/kbuild/makefiles.txt @@ -779,6 +779,13 @@ This will delete the directory debian, including all subdirectories. Kbuild will assume the directories to be in the same relative path as the Makefile if no absolute path is specified (path does not start with '/'). +To exclude certain files from make clean, use the $(no-clean-files) variable. +This is only a special case used in the top level Kbuild file: + + Example: + #Kbuild + no-clean-files := $(bounds-file) $(offsets-file) + Usually kbuild descends down in subdirectories due to "obj-* := dir/", but in the architecture makefiles where the kbuild infrastructure is not sufficient this sometimes needs to be explicit. -- cgit v1.2.3-70-g09d2 From 7974891db234467eaf1fec613ec0129cb4ac2332 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 28 Jun 2010 14:15:54 +0200 Subject: x86: Always use irq stacks IRQ stacks provide much better safety against unexpected stack use from interrupts, at the minimal downside of slightly higher memory usage. Enable irq stacks also for the default 8k stack on 32-bit kernels to minimize the problem of stack overflows through interrupt activity. This is what the 64-bit kernel and various other architectures already do. Signed-off-by: Christoph Hellwig LKML-Reference: <20100628121554.GA6605@lst.de> Signed-off-by: Thomas Gleixner --- Documentation/x86/x86_64/kernel-stacks | 6 +++--- arch/x86/Kconfig.debug | 3 +-- arch/x86/include/asm/irq.h | 12 +++++------- arch/x86/kernel/irq_32.c | 6 ------ 4 files changed, 9 insertions(+), 18 deletions(-) (limited to 'Documentation') diff --git a/Documentation/x86/x86_64/kernel-stacks b/Documentation/x86/x86_64/kernel-stacks index 5ad65d51fb95..a01eec5d1d0b 100644 --- a/Documentation/x86/x86_64/kernel-stacks +++ b/Documentation/x86/x86_64/kernel-stacks @@ -18,9 +18,9 @@ specialized stacks contain no useful data. The main CPU stacks are: Used for external hardware interrupts. If this is the first external hardware interrupt (i.e. not a nested hardware interrupt) then the kernel switches from the current task to the interrupt stack. Like - the split thread and interrupt stacks on i386 (with CONFIG_4KSTACKS), - this gives more room for kernel interrupt processing without having - to increase the size of every per thread stack. + the split thread and interrupt stacks on i386, this gives more room + for kernel interrupt processing without having to increase the size + of every per thread stack. The interrupt stack is also used when processing a softirq. diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 75085080b63e..badda8e20e78 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -128,8 +128,7 @@ config 4KSTACKS If you say Y here the kernel will use a 4Kb stacksize for the kernel stack attached to each process/thread. This facilitates running more threads on a system and also reduces the pressure - on the VM subsystem for higher order allocations. This option - will also use IRQ stacks to compensate for the reduced stackspace. + on the VM subsystem for higher order allocations. config DOUBLEFAULT default y diff --git a/arch/x86/include/asm/irq.h b/arch/x86/include/asm/irq.h index 5458380b6ef8..0bf5b0083650 100644 --- a/arch/x86/include/asm/irq.h +++ b/arch/x86/include/asm/irq.h @@ -19,18 +19,16 @@ static inline int irq_canonicalize(int irq) # define ARCH_HAS_NMI_WATCHDOG #endif -#ifdef CONFIG_4KSTACKS - extern void irq_ctx_init(int cpu); - extern void irq_ctx_exit(int cpu); -# define __ARCH_HAS_DO_SOFTIRQ +#ifdef CONFIG_X86_32 +extern void irq_ctx_init(int cpu); +extern void irq_ctx_exit(int cpu); #else # define irq_ctx_init(cpu) do { } while (0) # define irq_ctx_exit(cpu) do { } while (0) -# ifdef CONFIG_X86_64 -# define __ARCH_HAS_DO_SOFTIRQ -# endif #endif +#define __ARCH_HAS_DO_SOFTIRQ + #ifdef CONFIG_HOTPLUG_CPU #include extern void fixup_irqs(void); diff --git a/arch/x86/kernel/irq_32.c b/arch/x86/kernel/irq_32.c index 10709f29d166..67f5f9f5299f 100644 --- a/arch/x86/kernel/irq_32.c +++ b/arch/x86/kernel/irq_32.c @@ -49,7 +49,6 @@ static inline int check_stack_overflow(void) { return 0; } static inline void print_stack_overflow(void) { } #endif -#ifdef CONFIG_4KSTACKS /* * per-CPU IRQ handling contexts (thread information and stack) */ @@ -187,11 +186,6 @@ asmlinkage void do_softirq(void) local_irq_restore(flags); } -#else -static inline int -execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq) { return 0; } -#endif - bool handle_irq(unsigned irq, struct pt_regs *regs) { struct irq_desc *desc; -- cgit v1.2.3-70-g09d2 From 429d646d9aeafdb57717dab75c807215f5cce9c2 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Thu, 5 Aug 2010 10:44:48 +0200 Subject: Doc: Fix spelling Legned -> Legend mainatined -> maintained Legend is now Lenovo, so more fixes (text and url) would be possible, too. Cc: Jiri Kosina Cc: linux-doc@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Stefan Weil Signed-off-by: Jiri Kosina --- Documentation/arm/SA1100/FreeBird | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/arm/SA1100/FreeBird b/Documentation/arm/SA1100/FreeBird index fb23b770aaf4..ab9193663b2b 100644 --- a/Documentation/arm/SA1100/FreeBird +++ b/Documentation/arm/SA1100/FreeBird @@ -1,6 +1,6 @@ -Freebird-1.1 is produced by Legned(C) ,Inc. +Freebird-1.1 is produced by Legend(C), Inc. http://web.archive.org/web/*/http://www.legend.com.cn -and software/linux mainatined by Coventive(C),Inc. +and software/linux maintained by Coventive(C), Inc. (http://www.coventive.com) Based on the Nicolas's strongarm kernel tree. -- cgit v1.2.3-70-g09d2 From bbff2168c1088995753e889580c756971eca51e4 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 11 Aug 2010 18:34:09 +0200 Subject: paravirt: noreplace-paravirt is implemented for x86 and ia-64 kernel-parameters.txt lists 'noreplace-paravirt' parameter as being limited to X86-32, which is incorrect -- it's actually supported by x86-32, x86-64 and ia-64. Signed-off-by: Jiri Kosina Acked-by: Jeremy Fitzhardinge --- Documentation/kernel-parameters.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index d529b1363e95..8ed3a7772da1 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1777,7 +1777,7 @@ and is between 256 and 4096 characters. It is defined in the file norandmaps Don't use address space randomization. Equivalent to echo 0 > /proc/sys/kernel/randomize_va_space - noreplace-paravirt [X86-32,PV_OPS] Don't patch paravirt_ops + noreplace-paravirt [X86,IA-64,PV_OPS] Don't patch paravirt_ops noreplace-smp [X86-32,SMP] Don't replace SMP instructions with UP alternatives -- cgit v1.2.3-70-g09d2 From 2244d07bfa2097cb00600da91c715a8aa547917e Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 17 Aug 2010 08:59:14 +0000 Subject: net: simplify flags for tx timestamping This patch removes the abstraction introduced by the union skb_shared_tx in the shared skb data. The access of the different union elements at several places led to some confusion about accessing the shared tx_flags e.g. in skb_orphan_try(). http://marc.info/?l=linux-netdev&m=128084897415886&w=2 Signed-off-by: Oliver Hartkopp Signed-off-by: David S. Miller --- Documentation/networking/timestamping.txt | 22 +++++++++------- drivers/net/bfin_mac.c | 10 +++---- drivers/net/gianfar.c | 15 +++++------ drivers/net/igb/igb.h | 2 +- drivers/net/igb/igb_main.c | 11 ++++---- include/linux/skbuff.h | 44 +++++++++++-------------------- include/net/ip.h | 2 +- include/net/sock.h | 8 ++---- net/can/raw.c | 4 +-- net/core/dev.c | 6 ++--- net/core/skbuff.c | 2 +- net/ipv4/icmp.c | 4 +-- net/ipv4/ip_output.c | 6 ++--- net/ipv4/raw.c | 2 +- net/ipv4/udp.c | 4 +-- net/packet/af_packet.c | 4 +-- net/socket.c | 9 +++---- 17 files changed, 68 insertions(+), 87 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/timestamping.txt b/Documentation/networking/timestamping.txt index e8c8f4f06c67..98097d8cb910 100644 --- a/Documentation/networking/timestamping.txt +++ b/Documentation/networking/timestamping.txt @@ -172,15 +172,19 @@ struct skb_shared_hwtstamps { }; Time stamps for outgoing packets are to be generated as follows: -- In hard_start_xmit(), check if skb_tx(skb)->hardware is set no-zero. - If yes, then the driver is expected to do hardware time stamping. +- In hard_start_xmit(), check if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) + is set no-zero. If yes, then the driver is expected to do hardware time + stamping. - If this is possible for the skb and requested, then declare - that the driver is doing the time stamping by setting the field - skb_tx(skb)->in_progress non-zero. You might want to keep a pointer - to the associated skb for the next step and not free the skb. A driver - not supporting hardware time stamping doesn't do that. A driver must - never touch sk_buff::tstamp! It is used to store software generated - time stamps by the network subsystem. + that the driver is doing the time stamping by setting the flag + SKBTX_IN_PROGRESS in skb_shinfo(skb)->tx_flags , e.g. with + + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + + You might want to keep a pointer to the associated skb for the next step + and not free the skb. A driver not supporting hardware time stamping doesn't + do that. A driver must never touch sk_buff::tstamp! It is used to store + software generated time stamps by the network subsystem. - As soon as the driver has sent the packet and/or obtained a hardware time stamp for it, it passes the time stamp back by calling skb_hwtstamp_tx() with the original skb, the raw @@ -191,6 +195,6 @@ Time stamps for outgoing packets are to be generated as follows: this would occur at a later time in the processing pipeline than other software time stamping and therefore could lead to unexpected deltas between time stamps. -- If the driver did not call set skb_tx(skb)->in_progress, then +- If the driver did not set the SKBTX_IN_PROGRESS flag (see above), then dev_hard_start_xmit() checks whether software time stamping is wanted as fallback and potentially generates the time stamp. diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c index 012613fde3f4..7a0e4156fade 100644 --- a/drivers/net/bfin_mac.c +++ b/drivers/net/bfin_mac.c @@ -803,15 +803,14 @@ static void bfin_dump_hwtamp(char *s, ktime_t *hw, ktime_t *ts, struct timecompa static void bfin_tx_hwtstamp(struct net_device *netdev, struct sk_buff *skb) { struct bfin_mac_local *lp = netdev_priv(netdev); - union skb_shared_tx *shtx = skb_tx(skb); - if (shtx->hardware) { + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { int timeout_cnt = MAX_TIMEOUT_CNT; /* When doing time stamping, keep the connection to the socket * a while longer */ - shtx->in_progress = 1; + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; /* * The timestamping is done at the EMAC module's MII/RMII interface @@ -991,7 +990,6 @@ static int bfin_mac_hard_start_xmit(struct sk_buff *skb, struct bfin_mac_local *lp = netdev_priv(dev); u16 *data; u32 data_align = (unsigned long)(skb->data) & 0x3; - union skb_shared_tx *shtx = skb_tx(skb); current_tx_ptr->skb = skb; @@ -1005,7 +1003,7 @@ static int bfin_mac_hard_start_xmit(struct sk_buff *skb, * of this field are the length of the packet payload in bytes and the higher * 4 bits are the timestamping enable field. */ - if (shtx->hardware) + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) *data |= 0x1000; current_tx_ptr->desc_a.start_addr = (u32)data; @@ -1015,7 +1013,7 @@ static int bfin_mac_hard_start_xmit(struct sk_buff *skb, } else { *((u16 *)(current_tx_ptr->packet)) = (u16)(skb->len); /* enable timestamping for the sent packet */ - if (shtx->hardware) + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) *((u16 *)(current_tx_ptr->packet)) |= 0x1000; memcpy((u8 *)(current_tx_ptr->packet + 2), skb->data, skb->len); diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 3d9f958ebd2c..e6048d6ab0ea 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -2048,7 +2048,6 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) u32 bufaddr; unsigned long flags; unsigned int nr_frags, nr_txbds, length; - union skb_shared_tx *shtx; /* * TOE=1 frames larger than 2500 bytes may see excess delays @@ -2069,10 +2068,10 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) txq = netdev_get_tx_queue(dev, rq); base = tx_queue->tx_bd_base; regs = tx_queue->grp->regs; - shtx = skb_tx(skb); /* check if time stamp should be generated */ - if (unlikely(shtx->hardware && priv->hwts_tx_en)) + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && + priv->hwts_tx_en)) do_tstamp = 1; /* make space for additional header when fcb is needed */ @@ -2174,7 +2173,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Setup tx hardware time stamping if requested */ if (unlikely(do_tstamp)) { - shtx->in_progress = 1; + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; if (fcb == NULL) fcb = gfar_add_fcb(skb); fcb->ptp = 1; @@ -2446,7 +2445,6 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) int howmany = 0; u32 lstatus; size_t buflen; - union skb_shared_tx *shtx; rx_queue = priv->rx_queue[tx_queue->qindex]; bdp = tx_queue->dirty_tx; @@ -2461,8 +2459,7 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) * When time stamping, one additional TxBD must be freed. * Also, we need to dma_unmap_single() the TxPAL. */ - shtx = skb_tx(skb); - if (unlikely(shtx->in_progress)) + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) nr_txbds = frags + 2; else nr_txbds = frags + 1; @@ -2476,7 +2473,7 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) (lstatus & BD_LENGTH_MASK)) break; - if (unlikely(shtx->in_progress)) { + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) { next = next_txbd(bdp, base, tx_ring_size); buflen = next->length + GMAC_FCB_LEN; } else @@ -2485,7 +2482,7 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) dma_unmap_single(&priv->ofdev->dev, bdp->bufPtr, buflen, DMA_TO_DEVICE); - if (unlikely(shtx->in_progress)) { + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) { struct skb_shared_hwtstamps shhwtstamps; u64 *ns = (u64*) (((u32)skb->data + 0x10) & ~0x7); memset(&shhwtstamps, 0, sizeof(shhwtstamps)); diff --git a/drivers/net/igb/igb.h b/drivers/net/igb/igb.h index 6e63d9a7fc75..44e0ff1494e0 100644 --- a/drivers/net/igb/igb.h +++ b/drivers/net/igb/igb.h @@ -143,7 +143,7 @@ struct igb_buffer { u16 next_to_watch; unsigned int bytecount; u16 gso_segs; - union skb_shared_tx shtx; + u8 tx_flags; u8 mapped_as_page; }; /* RX */ diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index 9b4e5895f5f9..985e37cf17b6 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -3954,7 +3954,7 @@ static inline int igb_tx_map_adv(struct igb_ring *tx_ring, struct sk_buff *skb, } tx_ring->buffer_info[i].skb = skb; - tx_ring->buffer_info[i].shtx = skb_shinfo(skb)->tx_flags; + tx_ring->buffer_info[i].tx_flags = skb_shinfo(skb)->tx_flags; /* multiply data chunks by size of headers */ tx_ring->buffer_info[i].bytecount = ((gso_segs - 1) * hlen) + skb->len; tx_ring->buffer_info[i].gso_segs = gso_segs; @@ -4088,7 +4088,6 @@ netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *skb, u32 tx_flags = 0; u16 first; u8 hdr_len = 0; - union skb_shared_tx *shtx = skb_tx(skb); /* need: 1 descriptor per page, * + 2 desc gap to keep tail from touching head, @@ -4100,8 +4099,8 @@ netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *skb, return NETDEV_TX_BUSY; } - if (unlikely(shtx->hardware)) { - shtx->in_progress = 1; + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; tx_flags |= IGB_TX_FLAGS_TSTAMP; } @@ -5319,7 +5318,7 @@ static void igb_tx_hwtstamp(struct igb_q_vector *q_vector, struct igb_buffer *bu u64 regval; /* if skb does not support hw timestamp or TX stamp not valid exit */ - if (likely(!buffer_info->shtx.hardware) || + if (likely(!(buffer_info->tx_flags & SKBTX_HW_TSTAMP)) || !(rd32(E1000_TSYNCTXCTL) & E1000_TSYNCTXCTL_VALID)) return; @@ -5500,7 +5499,7 @@ static void igb_rx_hwtstamp(struct igb_q_vector *q_vector, u32 staterr, * values must belong to this one here and therefore we don't need to * compare any of the additional attributes stored for it. * - * If nothing went wrong, then it should have a skb_shared_tx that we + * If nothing went wrong, then it should have a shared tx_flags that we * can turn into a skb_shared_hwtstamps. */ if (staterr & E1000_RXDADV_STAT_TSIP) { diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d8050382b189..f067c95cf18a 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -163,26 +163,19 @@ struct skb_shared_hwtstamps { ktime_t syststamp; }; -/** - * struct skb_shared_tx - instructions for time stamping of outgoing packets - * @hardware: generate hardware time stamp - * @software: generate software time stamp - * @in_progress: device driver is going to provide - * hardware time stamp - * @prevent_sk_orphan: make sk reference available on driver level - * @flags: all shared_tx flags - * - * These flags are attached to packets as part of the - * &skb_shared_info. Use skb_tx() to get a pointer. - */ -union skb_shared_tx { - struct { - __u8 hardware:1, - software:1, - in_progress:1, - prevent_sk_orphan:1; - }; - __u8 flags; +/* Definitions for tx_flags in struct skb_shared_info */ +enum { + /* generate hardware time stamp */ + SKBTX_HW_TSTAMP = 1 << 0, + + /* generate software time stamp */ + SKBTX_SW_TSTAMP = 1 << 1, + + /* device driver is going to provide hardware time stamp */ + SKBTX_IN_PROGRESS = 1 << 2, + + /* ensure the originating sk reference is available on driver level */ + SKBTX_DRV_NEEDS_SK_REF = 1 << 3, }; /* This data is invariant across clones and lives at @@ -195,7 +188,7 @@ struct skb_shared_info { unsigned short gso_segs; unsigned short gso_type; __be32 ip6_frag_id; - union skb_shared_tx tx_flags; + __u8 tx_flags; struct sk_buff *frag_list; struct skb_shared_hwtstamps hwtstamps; @@ -587,11 +580,6 @@ static inline struct skb_shared_hwtstamps *skb_hwtstamps(struct sk_buff *skb) return &skb_shinfo(skb)->hwtstamps; } -static inline union skb_shared_tx *skb_tx(struct sk_buff *skb) -{ - return &skb_shinfo(skb)->tx_flags; -} - /** * skb_queue_empty - check if a queue is empty * @list: queue head @@ -1996,8 +1984,8 @@ extern void skb_tstamp_tx(struct sk_buff *orig_skb, static inline void sw_tx_timestamp(struct sk_buff *skb) { - union skb_shared_tx *shtx = skb_tx(skb); - if (shtx->software && !shtx->in_progress) + if (skb_shinfo(skb)->tx_flags & SKBTX_SW_TSTAMP && + !(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) skb_tstamp_tx(skb, NULL); } diff --git a/include/net/ip.h b/include/net/ip.h index 890f9725d681..7691aca133db 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -53,7 +53,7 @@ struct ipcm_cookie { __be32 addr; int oif; struct ip_options *opt; - union skb_shared_tx shtx; + __u8 tx_flags; }; #define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb)) diff --git a/include/net/sock.h b/include/net/sock.h index ac53bfbdfe16..100e43bf95fb 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1669,17 +1669,13 @@ static inline void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, /** * sock_tx_timestamp - checks whether the outgoing packet is to be time stamped - * @msg: outgoing packet * @sk: socket sending this packet - * @shtx: filled with instructions for time stamping + * @tx_flags: filled with instructions for time stamping * * Currently only depends on SOCK_TIMESTAMPING* flags. Returns error code if * parameters are invalid. */ -extern int sock_tx_timestamp(struct msghdr *msg, - struct sock *sk, - union skb_shared_tx *shtx); - +extern int sock_tx_timestamp(struct sock *sk, __u8 *tx_flags); /** * sk_eat_skb - Release a skb if it is no longer needed diff --git a/net/can/raw.c b/net/can/raw.c index a10e3338f084..7d77e67e57af 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -647,12 +647,12 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock, err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); if (err < 0) goto free_skb; - err = sock_tx_timestamp(msg, sk, skb_tx(skb)); + err = sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags); if (err < 0) goto free_skb; /* to be able to check the received tx sock reference in raw_rcv() */ - skb_tx(skb)->prevent_sk_orphan = 1; + skb_shinfo(skb)->tx_flags |= SKBTX_DRV_NEEDS_SK_REF; skb->dev = dev; skb->sk = sk; diff --git a/net/core/dev.c b/net/core/dev.c index 586a11cb4398..c1dc8a95f6ff 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1902,14 +1902,14 @@ static int dev_gso_segment(struct sk_buff *skb) /* * Try to orphan skb early, right before transmission by the device. - * We cannot orphan skb if tx timestamp is requested, since - * drivers need to call skb_tstamp_tx() to send the timestamp. + * We cannot orphan skb if tx timestamp is requested or the sk-reference + * is needed on driver level for other reasons, e.g. see net/can/raw.c */ static inline void skb_orphan_try(struct sk_buff *skb) { struct sock *sk = skb->sk; - if (sk && !skb_tx(skb)->flags) { + if (sk && !skb_shinfo(skb)->tx_flags) { /* skb_tx_hash() wont be able to get sk. * We copy sk_hash into skb->rxhash */ diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 3a2513f0d0c3..99ef721f773d 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3016,7 +3016,7 @@ void skb_tstamp_tx(struct sk_buff *orig_skb, } else { /* * no hardware time stamps available, - * so keep the skb_shared_tx and only + * so keep the shared tx_flags and only * store software time stamp */ skb->tstamp = ktime_get_real(); diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index a0d847c7cba5..96bc7f9475a3 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -379,7 +379,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) inet->tos = ip_hdr(skb)->tos; daddr = ipc.addr = rt->rt_src; ipc.opt = NULL; - ipc.shtx.flags = 0; + ipc.tx_flags = 0; if (icmp_param->replyopts.optlen) { ipc.opt = &icmp_param->replyopts; if (ipc.opt->srr) @@ -538,7 +538,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) inet_sk(sk)->tos = tos; ipc.addr = iph->saddr; ipc.opt = &icmp_param.replyopts; - ipc.shtx.flags = 0; + ipc.tx_flags = 0; { struct flowi fl = { diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 04b69896df5f..e807492f1777 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -953,7 +953,7 @@ alloc_new_skb: else /* only the initial fragment is time stamped */ - ipc->shtx.flags = 0; + ipc->tx_flags = 0; } if (skb == NULL) goto error; @@ -964,7 +964,7 @@ alloc_new_skb: skb->ip_summed = csummode; skb->csum = 0; skb_reserve(skb, hh_len); - *skb_tx(skb) = ipc->shtx; + skb_shinfo(skb)->tx_flags = ipc->tx_flags; /* * Find where to start putting bytes. @@ -1384,7 +1384,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar daddr = ipc.addr = rt->rt_src; ipc.opt = NULL; - ipc.shtx.flags = 0; + ipc.tx_flags = 0; if (replyopts.opt.optlen) { ipc.opt = &replyopts.opt; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 009a7b2aa1ef..1f85ef289895 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -505,7 +505,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ipc.addr = inet->inet_saddr; ipc.opt = NULL; - ipc.shtx.flags = 0; + ipc.tx_flags = 0; ipc.oif = sk->sk_bound_dev_if; if (msg->msg_controllen) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 32e0bef60d0a..86e757e162ee 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -797,7 +797,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, return -EOPNOTSUPP; ipc.opt = NULL; - ipc.shtx.flags = 0; + ipc.tx_flags = 0; if (up->pending) { /* @@ -845,7 +845,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ipc.addr = inet->inet_saddr; ipc.oif = sk->sk_bound_dev_if; - err = sock_tx_timestamp(msg, sk, &ipc.shtx); + err = sock_tx_timestamp(sk, &ipc.tx_flags); if (err) return err; if (msg->msg_controllen) { diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 9a17f28b1253..3616f27b9d46 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -488,7 +488,7 @@ retry: skb->dev = dev; skb->priority = sk->sk_priority; skb->mark = sk->sk_mark; - err = sock_tx_timestamp(msg, sk, skb_tx(skb)); + err = sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags); if (err < 0) goto out_unlock; @@ -1209,7 +1209,7 @@ static int packet_snd(struct socket *sock, err = skb_copy_datagram_from_iovec(skb, offset, msg->msg_iov, 0, len); if (err) goto out_free; - err = sock_tx_timestamp(msg, sk, skb_tx(skb)); + err = sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags); if (err < 0) goto out_free; diff --git a/net/socket.c b/net/socket.c index 2270b941bcc7..7848d12f5e4d 100644 --- a/net/socket.c +++ b/net/socket.c @@ -535,14 +535,13 @@ void sock_release(struct socket *sock) } EXPORT_SYMBOL(sock_release); -int sock_tx_timestamp(struct msghdr *msg, struct sock *sk, - union skb_shared_tx *shtx) +int sock_tx_timestamp(struct sock *sk, __u8 *tx_flags) { - shtx->flags = 0; + *tx_flags = 0; if (sock_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE)) - shtx->hardware = 1; + *tx_flags |= SKBTX_HW_TSTAMP; if (sock_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE)) - shtx->software = 1; + *tx_flags |= SKBTX_SW_TSTAMP; return 0; } EXPORT_SYMBOL(sock_tx_timestamp); -- cgit v1.2.3-70-g09d2 From 84483ea42cd4f2781d6e97a83ab3ebd0ff19fb10 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 16 Jun 2010 16:48:13 -0700 Subject: rcu: add shiny new debug assists to Documentation/RCU/checklist.txt Add a section describing PROVE_RCU, DEBUG_OBJECTS_RCU_HEAD, and the __rcu sparse checking to the RCU checklist. Suggested-by: David Miller Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- Documentation/RCU/checklist.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'Documentation') diff --git a/Documentation/RCU/checklist.txt b/Documentation/RCU/checklist.txt index 790d1a812376..c7c6788956f4 100644 --- a/Documentation/RCU/checklist.txt +++ b/Documentation/RCU/checklist.txt @@ -365,3 +365,26 @@ over a rather long period of time, but improvements are always welcome! and the compiler to freely reorder code into and out of RCU read-side critical sections. It is the responsibility of the RCU update-side primitives to deal with this. + +17. Use CONFIG_PROVE_RCU, CONFIG_DEBUG_OBJECTS_RCU_HEAD, and + the __rcu sparse checks to validate your RCU code. These + can help find problems as follows: + + CONFIG_PROVE_RCU: check that accesses to RCU-protected data + structures are carried out under the proper RCU + read-side critical section, while holding the right + combination of locks, or whatever other conditions + are appropriate. + + CONFIG_DEBUG_OBJECTS_RCU_HEAD: check that you don't pass the + same object to call_rcu() (or friends) before an RCU + grace period has elapsed since the last time that you + passed that same object to call_rcu() (or friends). + + __rcu sparse checks: tag the pointer to the RCU-protected data + structure with __rcu, and sparse will warn you if you + access that pointer without the services of one of the + variants of rcu_dereference(). + + These debugging aids can help you find problems that are + otherwise extremely difficult to spot. -- cgit v1.2.3-70-g09d2 From 65e423f8ee5843e1ea3f2d94adf4ba3560a17f7b Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 19 May 2010 10:42:16 -0700 Subject: Update documentation to note the passage of INIT_RCU_HEAD() Signed-off-by: Alexey Dobriyan Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- Documentation/DocBook/kernel-locking.tmpl | 8 -------- 1 file changed, 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/kernel-locking.tmpl b/Documentation/DocBook/kernel-locking.tmpl index 084f6ad7b7a0..e6cc57460212 100644 --- a/Documentation/DocBook/kernel-locking.tmpl +++ b/Documentation/DocBook/kernel-locking.tmpl @@ -1725,14 +1725,6 @@ the amount of locking which needs to be done. if (++cache_num > MAX_CACHE_SIZE) { struct object *i, *outcast = NULL; list_for_each_entry(i, &cache, list) { -@@ -85,6 +94,7 @@ - obj->popularity = 0; - atomic_set(&obj->refcnt, 1); /* The cache holds a reference */ - spin_lock_init(&obj->lock); -+ INIT_RCU_HEAD(&obj->rcu); - - spin_lock_irqsave(&cache_lock, flags); - __cache_add(obj); @@ -104,12 +114,11 @@ struct object *cache_find(int id) { -- cgit v1.2.3-70-g09d2 From ded5e5ed2f3348ba2f9a319c6497e46c22850e97 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 19 May 2010 10:46:55 -0700 Subject: Update call_rcu() usage, add synchronize_rcu() Reported-by: Kyle Hubert Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- Documentation/DocBook/kernel-locking.tmpl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/kernel-locking.tmpl b/Documentation/DocBook/kernel-locking.tmpl index e6cc57460212..ed64d220baf2 100644 --- a/Documentation/DocBook/kernel-locking.tmpl +++ b/Documentation/DocBook/kernel-locking.tmpl @@ -1645,7 +1645,9 @@ the amount of locking which needs to be done. all the readers who were traversing the list when we deleted the element are finished. We use call_rcu() to register a callback which will actually destroy the object once - the readers are finished. + all pre-existing readers are finished. Alternatively, + synchronize_rcu() may be used to block until + all pre-existing are finished. But how does Read Copy Update know when the readers are @@ -1714,7 +1716,7 @@ the amount of locking which needs to be done. - object_put(obj); + list_del_rcu(&obj->list); cache_num--; -+ call_rcu(&obj->rcu, cache_delete_rcu, obj); ++ call_rcu(&obj->rcu, cache_delete_rcu); } /* Must be holding cache_lock */ -- cgit v1.2.3-70-g09d2 From 5cc6517abdeccb6690b344a43b5ce8eaee82da3c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 13 Aug 2010 16:34:22 -0700 Subject: rcu: document ways of stalling updates in low-memory situations Signed-off-by: Paul E. McKenney --- Documentation/RCU/checklist.txt | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/RCU/checklist.txt b/Documentation/RCU/checklist.txt index c7c6788956f4..0c134f8afc6f 100644 --- a/Documentation/RCU/checklist.txt +++ b/Documentation/RCU/checklist.txt @@ -218,13 +218,22 @@ over a rather long period of time, but improvements are always welcome! include: a. Keeping a count of the number of data-structure elements - used by the RCU-protected data structure, including those - waiting for a grace period to elapse. Enforce a limit - on this number, stalling updates as needed to allow - previously deferred frees to complete. - - Alternatively, limit only the number awaiting deferred - free rather than the total number of elements. + used by the RCU-protected data structure, including + those waiting for a grace period to elapse. Enforce a + limit on this number, stalling updates as needed to allow + previously deferred frees to complete. Alternatively, + limit only the number awaiting deferred free rather than + the total number of elements. + + One way to stall the updates is to acquire the update-side + mutex. (Don't try this with a spinlock -- other CPUs + spinning on the lock could prevent the grace period + from ever ending.) Another way to stall the updates + is for the updates to use a wrapper function around + the memory allocator, so that this wrapper function + simulates OOM when there is too much memory awaiting an + RCU grace period. There are of course many other + variations on this theme. b. Limiting update rate. For example, if updates occur only once per hour, then no explicit rate limiting is required, -- cgit v1.2.3-70-g09d2 From 9863c90f682fba34cdc26c3437e8c00da6c83fa4 Mon Sep 17 00:00:00 2001 From: Alok Kataria Date: Mon, 23 Aug 2010 14:49:11 -0700 Subject: x86, vmware: Remove deprecated VMI kernel support With the recent innovations in CPU hardware acceleration technologies from Intel and AMD, VMware ran a few experiments to compare these techniques to guest paravirtualization technique on VMware's platform. These hardware assisted virtualization techniques have outperformed the performance benefits provided by VMI in most of the workloads. VMware expects that these hardware features will be ubiquitous in a couple of years, as a result, VMware has started a phased retirement of this feature from the hypervisor. Please note that VMI has always been an optimization and non-VMI kernels still work fine on VMware's platform. Latest versions of VMware's product which support VMI are, Workstation 7.0 and VSphere 4.0 on ESX side, future maintainence releases for these products will continue supporting VMI. For more details about VMI retirement take a look at this, http://blogs.vmware.com/guestosguide/2009/09/vmi-retirement.html This feature removal was scheduled for 2.6.37 back in September 2009. Signed-off-by: Alok N Kataria LKML-Reference: <1282600151.19396.22.camel@ank32.eng.vmware.com> Signed-off-by: H. Peter Anvin --- Documentation/feature-removal-schedule.txt | 28 - Documentation/kernel-parameters.txt | 2 +- arch/x86/Kconfig | 19 - arch/x86/include/asm/vmi.h | 269 --------- arch/x86/include/asm/vmi_time.h | 98 ---- arch/x86/kernel/Makefile | 1 - arch/x86/kernel/setup.c | 12 +- arch/x86/kernel/smpboot.c | 2 - arch/x86/kernel/vmi_32.c | 893 ----------------------------- arch/x86/kernel/vmiclock_32.c | 317 ---------- 10 files changed, 5 insertions(+), 1636 deletions(-) delete mode 100644 arch/x86/include/asm/vmi.h delete mode 100644 arch/x86/include/asm/vmi_time.h delete mode 100644 arch/x86/kernel/vmi_32.c delete mode 100644 arch/x86/kernel/vmiclock_32.c (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 842aa9de84a6..5e2bc4ab897a 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -386,34 +386,6 @@ Who: Tejun Heo ---------------------------- -What: Support for VMware's guest paravirtuliazation technique [VMI] will be - dropped. -When: 2.6.37 or earlier. -Why: With the recent innovations in CPU hardware acceleration technologies - from Intel and AMD, VMware ran a few experiments to compare these - techniques to guest paravirtualization technique on VMware's platform. - These hardware assisted virtualization techniques have outperformed the - performance benefits provided by VMI in most of the workloads. VMware - expects that these hardware features will be ubiquitous in a couple of - years, as a result, VMware has started a phased retirement of this - feature from the hypervisor. We will be removing this feature from the - Kernel too. Right now we are targeting 2.6.37 but can retire earlier if - technical reasons (read opportunity to remove major chunk of pvops) - arise. - - Please note that VMI has always been an optimization and non-VMI kernels - still work fine on VMware's platform. - Latest versions of VMware's product which support VMI are, - Workstation 7.0 and VSphere 4.0 on ESX side, future maintainence - releases for these products will continue supporting VMI. - - For more details about VMI retirement take a look at this, - http://blogs.vmware.com/guestosguide/2009/09/vmi-retirement.html - -Who: Alok N Kataria - ----------------------------- - What: Support for lcd_switch and display_get in asus-laptop driver When: March 2010 Why: These two features use non-standard interfaces. There are the diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 2c85c0692b01..582409801a41 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -455,7 +455,7 @@ and is between 256 and 4096 characters. It is defined in the file [ARM] imx_timer1,OSTS,netx_timer,mpu_timer2, pxa_timer,timer3,32k_counter,timer0_1 [AVR32] avr32 - [X86-32] pit,hpet,tsc,vmi-timer; + [X86-32] pit,hpet,tsc; scx200_hrt on Geode; cyclone on IBM x440 [MIPS] MIPS [PARISC] cr16 diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index cea0cd9a316f..f0ee331feeab 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -517,25 +517,6 @@ if PARAVIRT_GUEST source "arch/x86/xen/Kconfig" -config VMI - bool "VMI Guest support (DEPRECATED)" - select PARAVIRT - depends on X86_32 - ---help--- - VMI provides a paravirtualized interface to the VMware ESX server - (it could be used by other hypervisors in theory too, but is not - at the moment), by linking the kernel to a GPL-ed ROM module - provided by the hypervisor. - - As of September 2009, VMware has started a phased retirement - of this feature from VMware's products. Please see - feature-removal-schedule.txt for details. If you are - planning to enable this option, please note that you cannot - live migrate a VMI enabled VM to a future VMware product, - which doesn't support VMI. So if you expect your kernel to - seamlessly migrate to newer VMware products, keep this - disabled. - config KVM_CLOCK bool "KVM paravirtualized clock" select PARAVIRT diff --git a/arch/x86/include/asm/vmi.h b/arch/x86/include/asm/vmi.h deleted file mode 100644 index 61e08c0a2907..000000000000 --- a/arch/x86/include/asm/vmi.h +++ /dev/null @@ -1,269 +0,0 @@ -/* - * VMI interface definition - * - * Copyright (C) 2005, VMware, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Maintained by: Zachary Amsden zach@vmware.com - * - */ -#include - -/* - *--------------------------------------------------------------------- - * - * VMI Option ROM API - * - *--------------------------------------------------------------------- - */ -#define VMI_SIGNATURE 0x696d5663 /* "cVmi" */ - -#define PCI_VENDOR_ID_VMWARE 0x15AD -#define PCI_DEVICE_ID_VMWARE_VMI 0x0801 - -/* - * We use two version numbers for compatibility, with the major - * number signifying interface breakages, and the minor number - * interface extensions. - */ -#define VMI_API_REV_MAJOR 3 -#define VMI_API_REV_MINOR 0 - -#define VMI_CALL_CPUID 0 -#define VMI_CALL_WRMSR 1 -#define VMI_CALL_RDMSR 2 -#define VMI_CALL_SetGDT 3 -#define VMI_CALL_SetLDT 4 -#define VMI_CALL_SetIDT 5 -#define VMI_CALL_SetTR 6 -#define VMI_CALL_GetGDT 7 -#define VMI_CALL_GetLDT 8 -#define VMI_CALL_GetIDT 9 -#define VMI_CALL_GetTR 10 -#define VMI_CALL_WriteGDTEntry 11 -#define VMI_CALL_WriteLDTEntry 12 -#define VMI_CALL_WriteIDTEntry 13 -#define VMI_CALL_UpdateKernelStack 14 -#define VMI_CALL_SetCR0 15 -#define VMI_CALL_SetCR2 16 -#define VMI_CALL_SetCR3 17 -#define VMI_CALL_SetCR4 18 -#define VMI_CALL_GetCR0 19 -#define VMI_CALL_GetCR2 20 -#define VMI_CALL_GetCR3 21 -#define VMI_CALL_GetCR4 22 -#define VMI_CALL_WBINVD 23 -#define VMI_CALL_SetDR 24 -#define VMI_CALL_GetDR 25 -#define VMI_CALL_RDPMC 26 -#define VMI_CALL_RDTSC 27 -#define VMI_CALL_CLTS 28 -#define VMI_CALL_EnableInterrupts 29 -#define VMI_CALL_DisableInterrupts 30 -#define VMI_CALL_GetInterruptMask 31 -#define VMI_CALL_SetInterruptMask 32 -#define VMI_CALL_IRET 33 -#define VMI_CALL_SYSEXIT 34 -#define VMI_CALL_Halt 35 -#define VMI_CALL_Reboot 36 -#define VMI_CALL_Shutdown 37 -#define VMI_CALL_SetPxE 38 -#define VMI_CALL_SetPxELong 39 -#define VMI_CALL_UpdatePxE 40 -#define VMI_CALL_UpdatePxELong 41 -#define VMI_CALL_MachineToPhysical 42 -#define VMI_CALL_PhysicalToMachine 43 -#define VMI_CALL_AllocatePage 44 -#define VMI_CALL_ReleasePage 45 -#define VMI_CALL_InvalPage 46 -#define VMI_CALL_FlushTLB 47 -#define VMI_CALL_SetLinearMapping 48 - -#define VMI_CALL_SetIOPLMask 61 -#define VMI_CALL_SetInitialAPState 62 -#define VMI_CALL_APICWrite 63 -#define VMI_CALL_APICRead 64 -#define VMI_CALL_IODelay 65 -#define VMI_CALL_SetLazyMode 73 - -/* - *--------------------------------------------------------------------- - * - * MMU operation flags - * - *--------------------------------------------------------------------- - */ - -/* Flags used by VMI_{Allocate|Release}Page call */ -#define VMI_PAGE_PAE 0x10 /* Allocate PAE shadow */ -#define VMI_PAGE_CLONE 0x20 /* Clone from another shadow */ -#define VMI_PAGE_ZEROED 0x40 /* Page is pre-zeroed */ - - -/* Flags shared by Allocate|Release Page and PTE updates */ -#define VMI_PAGE_PT 0x01 -#define VMI_PAGE_PD 0x02 -#define VMI_PAGE_PDP 0x04 -#define VMI_PAGE_PML4 0x08 - -#define VMI_PAGE_NORMAL 0x00 /* for debugging */ - -/* Flags used by PTE updates */ -#define VMI_PAGE_CURRENT_AS 0x10 /* implies VMI_PAGE_VA_MASK is valid */ -#define VMI_PAGE_DEFER 0x20 /* may queue update until TLB inval */ -#define VMI_PAGE_VA_MASK 0xfffff000 - -#ifdef CONFIG_X86_PAE -#define VMI_PAGE_L1 (VMI_PAGE_PT | VMI_PAGE_PAE | VMI_PAGE_ZEROED) -#define VMI_PAGE_L2 (VMI_PAGE_PD | VMI_PAGE_PAE | VMI_PAGE_ZEROED) -#else -#define VMI_PAGE_L1 (VMI_PAGE_PT | VMI_PAGE_ZEROED) -#define VMI_PAGE_L2 (VMI_PAGE_PD | VMI_PAGE_ZEROED) -#endif - -/* Flags used by VMI_FlushTLB call */ -#define VMI_FLUSH_TLB 0x01 -#define VMI_FLUSH_GLOBAL 0x02 - -/* - *--------------------------------------------------------------------- - * - * VMI relocation definitions for ROM call get_reloc - * - *--------------------------------------------------------------------- - */ - -/* VMI Relocation types */ -#define VMI_RELOCATION_NONE 0 -#define VMI_RELOCATION_CALL_REL 1 -#define VMI_RELOCATION_JUMP_REL 2 -#define VMI_RELOCATION_NOP 3 - -#ifndef __ASSEMBLY__ -struct vmi_relocation_info { - unsigned char *eip; - unsigned char type; - unsigned char reserved[3]; -}; -#endif - - -/* - *--------------------------------------------------------------------- - * - * Generic ROM structures and definitions - * - *--------------------------------------------------------------------- - */ - -#ifndef __ASSEMBLY__ - -struct vrom_header { - u16 rom_signature; /* option ROM signature */ - u8 rom_length; /* ROM length in 512 byte chunks */ - u8 rom_entry[4]; /* 16-bit code entry point */ - u8 rom_pad0; /* 4-byte align pad */ - u32 vrom_signature; /* VROM identification signature */ - u8 api_version_min;/* Minor version of API */ - u8 api_version_maj;/* Major version of API */ - u8 jump_slots; /* Number of jump slots */ - u8 reserved1; /* Reserved for expansion */ - u32 virtual_top; /* Hypervisor virtual address start */ - u16 reserved2; /* Reserved for expansion */ - u16 license_offs; /* Offset to License string */ - u16 pci_header_offs;/* Offset to PCI OPROM header */ - u16 pnp_header_offs;/* Offset to PnP OPROM header */ - u32 rom_pad3; /* PnP reserverd / VMI reserved */ - u8 reserved[96]; /* Reserved for headers */ - char vmi_init[8]; /* VMI_Init jump point */ - char get_reloc[8]; /* VMI_GetRelocationInfo jump point */ -} __attribute__((packed)); - -struct pnp_header { - char sig[4]; - char rev; - char size; - short next; - short res; - long devID; - unsigned short manufacturer_offset; - unsigned short product_offset; -} __attribute__((packed)); - -struct pci_header { - char sig[4]; - short vendorID; - short deviceID; - short vpdData; - short size; - char rev; - char class; - char subclass; - char interface; - short chunks; - char rom_version_min; - char rom_version_maj; - char codetype; - char lastRom; - short reserved; -} __attribute__((packed)); - -/* Function prototypes for bootstrapping */ -#ifdef CONFIG_VMI -extern void vmi_init(void); -extern void vmi_activate(void); -extern void vmi_bringup(void); -#else -static inline void vmi_init(void) {} -static inline void vmi_activate(void) {} -static inline void vmi_bringup(void) {} -#endif - -/* State needed to start an application processor in an SMP system. */ -struct vmi_ap_state { - u32 cr0; - u32 cr2; - u32 cr3; - u32 cr4; - - u64 efer; - - u32 eip; - u32 eflags; - u32 eax; - u32 ebx; - u32 ecx; - u32 edx; - u32 esp; - u32 ebp; - u32 esi; - u32 edi; - u16 cs; - u16 ss; - u16 ds; - u16 es; - u16 fs; - u16 gs; - u16 ldtr; - - u16 gdtr_limit; - u32 gdtr_base; - u32 idtr_base; - u16 idtr_limit; -}; - -#endif diff --git a/arch/x86/include/asm/vmi_time.h b/arch/x86/include/asm/vmi_time.h deleted file mode 100644 index c6e0bee93e3c..000000000000 --- a/arch/x86/include/asm/vmi_time.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * VMI Time wrappers - * - * Copyright (C) 2006, VMware, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to dhecht@vmware.com - * - */ - -#ifndef _ASM_X86_VMI_TIME_H -#define _ASM_X86_VMI_TIME_H - -/* - * Raw VMI call indices for timer functions - */ -#define VMI_CALL_GetCycleFrequency 66 -#define VMI_CALL_GetCycleCounter 67 -#define VMI_CALL_SetAlarm 68 -#define VMI_CALL_CancelAlarm 69 -#define VMI_CALL_GetWallclockTime 70 -#define VMI_CALL_WallclockUpdated 71 - -/* Cached VMI timer operations */ -extern struct vmi_timer_ops { - u64 (*get_cycle_frequency)(void); - u64 (*get_cycle_counter)(int); - u64 (*get_wallclock)(void); - int (*wallclock_updated)(void); - void (*set_alarm)(u32 flags, u64 expiry, u64 period); - void (*cancel_alarm)(u32 flags); -} vmi_timer_ops; - -/* Prototypes */ -extern void __init vmi_time_init(void); -extern unsigned long vmi_get_wallclock(void); -extern int vmi_set_wallclock(unsigned long now); -extern unsigned long long vmi_sched_clock(void); -extern unsigned long vmi_tsc_khz(void); - -#ifdef CONFIG_X86_LOCAL_APIC -extern void __devinit vmi_time_bsp_init(void); -extern void __devinit vmi_time_ap_init(void); -#endif - -/* - * When run under a hypervisor, a vcpu is always in one of three states: - * running, halted, or ready. The vcpu is in the 'running' state if it - * is executing. When the vcpu executes the halt interface, the vcpu - * enters the 'halted' state and remains halted until there is some work - * pending for the vcpu (e.g. an alarm expires, host I/O completes on - * behalf of virtual I/O). At this point, the vcpu enters the 'ready' - * state (waiting for the hypervisor to reschedule it). Finally, at any - * time when the vcpu is not in the 'running' state nor the 'halted' - * state, it is in the 'ready' state. - * - * Real time is advances while the vcpu is 'running', 'ready', or - * 'halted'. Stolen time is the time in which the vcpu is in the - * 'ready' state. Available time is the remaining time -- the vcpu is - * either 'running' or 'halted'. - * - * All three views of time are accessible through the VMI cycle - * counters. - */ - -/* The cycle counters. */ -#define VMI_CYCLES_REAL 0 -#define VMI_CYCLES_AVAILABLE 1 -#define VMI_CYCLES_STOLEN 2 - -/* The alarm interface 'flags' bits */ -#define VMI_ALARM_COUNTERS 2 - -#define VMI_ALARM_COUNTER_MASK 0x000000ff - -#define VMI_ALARM_WIRED_IRQ0 0x00000000 -#define VMI_ALARM_WIRED_LVTT 0x00010000 - -#define VMI_ALARM_IS_ONESHOT 0x00000000 -#define VMI_ALARM_IS_PERIODIC 0x00000100 - -#define CONFIG_VMI_ALARM_HZ 100 - -#endif /* _ASM_X86_VMI_TIME_H */ diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 0925676266bd..801127cd9754 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -91,7 +91,6 @@ obj-$(CONFIG_K8_NB) += k8.o obj-$(CONFIG_DEBUG_RODATA_TEST) += test_rodata.o obj-$(CONFIG_DEBUG_NX_TEST) += test_nx.o -obj-$(CONFIG_VMI) += vmi_32.o vmiclock_32.o obj-$(CONFIG_KVM_GUEST) += kvm.o obj-$(CONFIG_KVM_CLOCK) += kvmclock.o obj-$(CONFIG_PARAVIRT) += paravirt.o paravirt_patch_$(BITS).o diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index c3a4fbb2b996..feb4c21e499a 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -83,7 +83,6 @@ #include #include #include -#include #include #include #include @@ -734,10 +733,10 @@ void __init setup_arch(char **cmdline_p) printk(KERN_INFO "Command line: %s\n", boot_command_line); #endif - /* VMI may relocate the fixmap; do this before touching ioremap area */ - vmi_init(); - - /* OFW also may relocate the fixmap */ + /* + * If we have OLPC OFW, we might end up relocating the fixmap due to + * reserve_top(), so do this before touching the ioremap area. + */ olpc_ofw_detect(); early_trap_init(); @@ -838,9 +837,6 @@ void __init setup_arch(char **cmdline_p) x86_report_nx(); - /* Must be before kernel pagetables are setup */ - vmi_activate(); - /* after early param, so could get panic from serial */ reserve_early_setup_data(); diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 8b3bfc4dd708..63a1a5596ac0 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -62,7 +62,6 @@ #include #include #include -#include #include #include #include @@ -311,7 +310,6 @@ notrace static void __cpuinit start_secondary(void *unused) __flush_tlb_all(); #endif - vmi_bringup(); cpu_init(); preempt_disable(); smp_callin(); diff --git a/arch/x86/kernel/vmi_32.c b/arch/x86/kernel/vmi_32.c deleted file mode 100644 index ce9fbacb7526..000000000000 --- a/arch/x86/kernel/vmi_32.c +++ /dev/null @@ -1,893 +0,0 @@ -/* - * VMI specific paravirt-ops implementation - * - * Copyright (C) 2005, VMware, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Send feedback to zach@vmware.com - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Convenient for calling VMI functions indirectly in the ROM */ -typedef u32 __attribute__((regparm(1))) (VROMFUNC)(void); -typedef u64 __attribute__((regparm(2))) (VROMLONGFUNC)(int); - -#define call_vrom_func(rom,func) \ - (((VROMFUNC *)(rom->func))()) - -#define call_vrom_long_func(rom,func,arg) \ - (((VROMLONGFUNC *)(rom->func)) (arg)) - -static struct vrom_header *vmi_rom; -static int disable_pge; -static int disable_pse; -static int disable_sep; -static int disable_tsc; -static int disable_mtrr; -static int disable_noidle; -static int disable_vmi_timer; - -/* Cached VMI operations */ -static struct { - void (*cpuid)(void /* non-c */); - void (*_set_ldt)(u32 selector); - void (*set_tr)(u32 selector); - void (*write_idt_entry)(struct desc_struct *, int, u32, u32); - void (*write_gdt_entry)(struct desc_struct *, int, u32, u32); - void (*write_ldt_entry)(struct desc_struct *, int, u32, u32); - void (*set_kernel_stack)(u32 selector, u32 sp0); - void (*allocate_page)(u32, u32, u32, u32, u32); - void (*release_page)(u32, u32); - void (*set_pte)(pte_t, pte_t *, unsigned); - void (*update_pte)(pte_t *, unsigned); - void (*set_linear_mapping)(int, void *, u32, u32); - void (*_flush_tlb)(int); - void (*set_initial_ap_state)(int, int); - void (*halt)(void); - void (*set_lazy_mode)(int mode); -} vmi_ops; - -/* Cached VMI operations */ -struct vmi_timer_ops vmi_timer_ops; - -/* - * VMI patching routines. - */ -#define MNEM_CALL 0xe8 -#define MNEM_JMP 0xe9 -#define MNEM_RET 0xc3 - -#define IRQ_PATCH_INT_MASK 0 -#define IRQ_PATCH_DISABLE 5 - -static inline void patch_offset(void *insnbuf, - unsigned long ip, unsigned long dest) -{ - *(unsigned long *)(insnbuf+1) = dest-ip-5; -} - -static unsigned patch_internal(int call, unsigned len, void *insnbuf, - unsigned long ip) -{ - u64 reloc; - struct vmi_relocation_info *const rel = (struct vmi_relocation_info *)&reloc; - reloc = call_vrom_long_func(vmi_rom, get_reloc, call); - switch(rel->type) { - case VMI_RELOCATION_CALL_REL: - BUG_ON(len < 5); - *(char *)insnbuf = MNEM_CALL; - patch_offset(insnbuf, ip, (unsigned long)rel->eip); - return 5; - - case VMI_RELOCATION_JUMP_REL: - BUG_ON(len < 5); - *(char *)insnbuf = MNEM_JMP; - patch_offset(insnbuf, ip, (unsigned long)rel->eip); - return 5; - - case VMI_RELOCATION_NOP: - /* obliterate the whole thing */ - return 0; - - case VMI_RELOCATION_NONE: - /* leave native code in place */ - break; - - default: - BUG(); - } - return len; -} - -/* - * Apply patch if appropriate, return length of new instruction - * sequence. The callee does nop padding for us. - */ -static unsigned vmi_patch(u8 type, u16 clobbers, void *insns, - unsigned long ip, unsigned len) -{ - switch (type) { - case PARAVIRT_PATCH(pv_irq_ops.irq_disable): - return patch_internal(VMI_CALL_DisableInterrupts, len, - insns, ip); - case PARAVIRT_PATCH(pv_irq_ops.irq_enable): - return patch_internal(VMI_CALL_EnableInterrupts, len, - insns, ip); - case PARAVIRT_PATCH(pv_irq_ops.restore_fl): - return patch_internal(VMI_CALL_SetInterruptMask, len, - insns, ip); - case PARAVIRT_PATCH(pv_irq_ops.save_fl): - return patch_internal(VMI_CALL_GetInterruptMask, len, - insns, ip); - case PARAVIRT_PATCH(pv_cpu_ops.iret): - return patch_internal(VMI_CALL_IRET, len, insns, ip); - case PARAVIRT_PATCH(pv_cpu_ops.irq_enable_sysexit): - return patch_internal(VMI_CALL_SYSEXIT, len, insns, ip); - default: - break; - } - return len; -} - -/* CPUID has non-C semantics, and paravirt-ops API doesn't match hardware ISA */ -static void vmi_cpuid(unsigned int *ax, unsigned int *bx, - unsigned int *cx, unsigned int *dx) -{ - int override = 0; - if (*ax == 1) - override = 1; - asm volatile ("call *%6" - : "=a" (*ax), - "=b" (*bx), - "=c" (*cx), - "=d" (*dx) - : "0" (*ax), "2" (*cx), "r" (vmi_ops.cpuid)); - if (override) { - if (disable_pse) - *dx &= ~X86_FEATURE_PSE; - if (disable_pge) - *dx &= ~X86_FEATURE_PGE; - if (disable_sep) - *dx &= ~X86_FEATURE_SEP; - if (disable_tsc) - *dx &= ~X86_FEATURE_TSC; - if (disable_mtrr) - *dx &= ~X86_FEATURE_MTRR; - } -} - -static inline void vmi_maybe_load_tls(struct desc_struct *gdt, int nr, struct desc_struct *new) -{ - if (gdt[nr].a != new->a || gdt[nr].b != new->b) - write_gdt_entry(gdt, nr, new, 0); -} - -static void vmi_load_tls(struct thread_struct *t, unsigned int cpu) -{ - struct desc_struct *gdt = get_cpu_gdt_table(cpu); - vmi_maybe_load_tls(gdt, GDT_ENTRY_TLS_MIN + 0, &t->tls_array[0]); - vmi_maybe_load_tls(gdt, GDT_ENTRY_TLS_MIN + 1, &t->tls_array[1]); - vmi_maybe_load_tls(gdt, GDT_ENTRY_TLS_MIN + 2, &t->tls_array[2]); -} - -static void vmi_set_ldt(const void *addr, unsigned entries) -{ - unsigned cpu = smp_processor_id(); - struct desc_struct desc; - - pack_descriptor(&desc, (unsigned long)addr, - entries * sizeof(struct desc_struct) - 1, - DESC_LDT, 0); - write_gdt_entry(get_cpu_gdt_table(cpu), GDT_ENTRY_LDT, &desc, DESC_LDT); - vmi_ops._set_ldt(entries ? GDT_ENTRY_LDT*sizeof(struct desc_struct) : 0); -} - -static void vmi_set_tr(void) -{ - vmi_ops.set_tr(GDT_ENTRY_TSS*sizeof(struct desc_struct)); -} - -static void vmi_write_idt_entry(gate_desc *dt, int entry, const gate_desc *g) -{ - u32 *idt_entry = (u32 *)g; - vmi_ops.write_idt_entry(dt, entry, idt_entry[0], idt_entry[1]); -} - -static void vmi_write_gdt_entry(struct desc_struct *dt, int entry, - const void *desc, int type) -{ - u32 *gdt_entry = (u32 *)desc; - vmi_ops.write_gdt_entry(dt, entry, gdt_entry[0], gdt_entry[1]); -} - -static void vmi_write_ldt_entry(struct desc_struct *dt, int entry, - const void *desc) -{ - u32 *ldt_entry = (u32 *)desc; - vmi_ops.write_ldt_entry(dt, entry, ldt_entry[0], ldt_entry[1]); -} - -static void vmi_load_sp0(struct tss_struct *tss, - struct thread_struct *thread) -{ - tss->x86_tss.sp0 = thread->sp0; - - /* This can only happen when SEP is enabled, no need to test "SEP"arately */ - if (unlikely(tss->x86_tss.ss1 != thread->sysenter_cs)) { - tss->x86_tss.ss1 = thread->sysenter_cs; - wrmsr(MSR_IA32_SYSENTER_CS, thread->sysenter_cs, 0); - } - vmi_ops.set_kernel_stack(__KERNEL_DS, tss->x86_tss.sp0); -} - -static void vmi_flush_tlb_user(void) -{ - vmi_ops._flush_tlb(VMI_FLUSH_TLB); -} - -static void vmi_flush_tlb_kernel(void) -{ - vmi_ops._flush_tlb(VMI_FLUSH_TLB | VMI_FLUSH_GLOBAL); -} - -/* Stub to do nothing at all; used for delays and unimplemented calls */ -static void vmi_nop(void) -{ -} - -static void vmi_allocate_pte(struct mm_struct *mm, unsigned long pfn) -{ - vmi_ops.allocate_page(pfn, VMI_PAGE_L1, 0, 0, 0); -} - -static void vmi_allocate_pmd(struct mm_struct *mm, unsigned long pfn) -{ - /* - * This call comes in very early, before mem_map is setup. - * It is called only for swapper_pg_dir, which already has - * data on it. - */ - vmi_ops.allocate_page(pfn, VMI_PAGE_L2, 0, 0, 0); -} - -static void vmi_allocate_pmd_clone(unsigned long pfn, unsigned long clonepfn, unsigned long start, unsigned long count) -{ - vmi_ops.allocate_page(pfn, VMI_PAGE_L2 | VMI_PAGE_CLONE, clonepfn, start, count); -} - -static void vmi_release_pte(unsigned long pfn) -{ - vmi_ops.release_page(pfn, VMI_PAGE_L1); -} - -static void vmi_release_pmd(unsigned long pfn) -{ - vmi_ops.release_page(pfn, VMI_PAGE_L2); -} - -/* - * We use the pgd_free hook for releasing the pgd page: - */ -static void vmi_pgd_free(struct mm_struct *mm, pgd_t *pgd) -{ - unsigned long pfn = __pa(pgd) >> PAGE_SHIFT; - - vmi_ops.release_page(pfn, VMI_PAGE_L2); -} - -/* - * Helper macros for MMU update flags. We can defer updates until a flush - * or page invalidation only if the update is to the current address space - * (otherwise, there is no flush). We must check against init_mm, since - * this could be a kernel update, which usually passes init_mm, although - * sometimes this check can be skipped if we know the particular function - * is only called on user mode PTEs. We could change the kernel to pass - * current->active_mm here, but in particular, I was unsure if changing - * mm/highmem.c to do this would still be correct on other architectures. - */ -#define is_current_as(mm, mustbeuser) ((mm) == current->active_mm || \ - (!mustbeuser && (mm) == &init_mm)) -#define vmi_flags_addr(mm, addr, level, user) \ - ((level) | (is_current_as(mm, user) ? \ - (VMI_PAGE_CURRENT_AS | ((addr) & VMI_PAGE_VA_MASK)) : 0)) -#define vmi_flags_addr_defer(mm, addr, level, user) \ - ((level) | (is_current_as(mm, user) ? \ - (VMI_PAGE_DEFER | VMI_PAGE_CURRENT_AS | ((addr) & VMI_PAGE_VA_MASK)) : 0)) - -static void vmi_update_pte(struct mm_struct *mm, unsigned long addr, pte_t *ptep) -{ - vmi_ops.update_pte(ptep, vmi_flags_addr(mm, addr, VMI_PAGE_PT, 0)); -} - -static void vmi_update_pte_defer(struct mm_struct *mm, unsigned long addr, pte_t *ptep) -{ - vmi_ops.update_pte(ptep, vmi_flags_addr_defer(mm, addr, VMI_PAGE_PT, 0)); -} - -static void vmi_set_pte(pte_t *ptep, pte_t pte) -{ - /* XXX because of set_pmd_pte, this can be called on PT or PD layers */ - vmi_ops.set_pte(pte, ptep, VMI_PAGE_PT); -} - -static void vmi_set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte) -{ - vmi_ops.set_pte(pte, ptep, vmi_flags_addr(mm, addr, VMI_PAGE_PT, 0)); -} - -static void vmi_set_pmd(pmd_t *pmdp, pmd_t pmdval) -{ -#ifdef CONFIG_X86_PAE - const pte_t pte = { .pte = pmdval.pmd }; -#else - const pte_t pte = { pmdval.pud.pgd.pgd }; -#endif - vmi_ops.set_pte(pte, (pte_t *)pmdp, VMI_PAGE_PD); -} - -#ifdef CONFIG_X86_PAE - -static void vmi_set_pte_atomic(pte_t *ptep, pte_t pteval) -{ - /* - * XXX This is called from set_pmd_pte, but at both PT - * and PD layers so the VMI_PAGE_PT flag is wrong. But - * it is only called for large page mapping changes, - * the Xen backend, doesn't support large pages, and the - * ESX backend doesn't depend on the flag. - */ - set_64bit((unsigned long long *)ptep,pte_val(pteval)); - vmi_ops.update_pte(ptep, VMI_PAGE_PT); -} - -static void vmi_set_pud(pud_t *pudp, pud_t pudval) -{ - /* Um, eww */ - const pte_t pte = { .pte = pudval.pgd.pgd }; - vmi_ops.set_pte(pte, (pte_t *)pudp, VMI_PAGE_PDP); -} - -static void vmi_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) -{ - const pte_t pte = { .pte = 0 }; - vmi_ops.set_pte(pte, ptep, vmi_flags_addr(mm, addr, VMI_PAGE_PT, 0)); -} - -static void vmi_pmd_clear(pmd_t *pmd) -{ - const pte_t pte = { .pte = 0 }; - vmi_ops.set_pte(pte, (pte_t *)pmd, VMI_PAGE_PD); -} -#endif - -#ifdef CONFIG_SMP -static void __devinit -vmi_startup_ipi_hook(int phys_apicid, unsigned long start_eip, - unsigned long start_esp) -{ - struct vmi_ap_state ap; - - /* Default everything to zero. This is fine for most GPRs. */ - memset(&ap, 0, sizeof(struct vmi_ap_state)); - - ap.gdtr_limit = GDT_SIZE - 1; - ap.gdtr_base = (unsigned long) get_cpu_gdt_table(phys_apicid); - - ap.idtr_limit = IDT_ENTRIES * 8 - 1; - ap.idtr_base = (unsigned long) idt_table; - - ap.ldtr = 0; - - ap.cs = __KERNEL_CS; - ap.eip = (unsigned long) start_eip; - ap.ss = __KERNEL_DS; - ap.esp = (unsigned long) start_esp; - - ap.ds = __USER_DS; - ap.es = __USER_DS; - ap.fs = __KERNEL_PERCPU; - ap.gs = __KERNEL_STACK_CANARY; - - ap.eflags = 0; - -#ifdef CONFIG_X86_PAE - /* efer should match BSP efer. */ - if (cpu_has_nx) { - unsigned l, h; - rdmsr(MSR_EFER, l, h); - ap.efer = (unsigned long long) h << 32 | l; - } -#endif - - ap.cr3 = __pa(swapper_pg_dir); - /* Protected mode, paging, AM, WP, NE, MP. */ - ap.cr0 = 0x80050023; - ap.cr4 = mmu_cr4_features; - vmi_ops.set_initial_ap_state((u32)&ap, phys_apicid); -} -#endif - -static void vmi_start_context_switch(struct task_struct *prev) -{ - paravirt_start_context_switch(prev); - vmi_ops.set_lazy_mode(2); -} - -static void vmi_end_context_switch(struct task_struct *next) -{ - vmi_ops.set_lazy_mode(0); - paravirt_end_context_switch(next); -} - -static void vmi_enter_lazy_mmu(void) -{ - paravirt_enter_lazy_mmu(); - vmi_ops.set_lazy_mode(1); -} - -static void vmi_leave_lazy_mmu(void) -{ - vmi_ops.set_lazy_mode(0); - paravirt_leave_lazy_mmu(); -} - -static inline int __init check_vmi_rom(struct vrom_header *rom) -{ - struct pci_header *pci; - struct pnp_header *pnp; - const char *manufacturer = "UNKNOWN"; - const char *product = "UNKNOWN"; - const char *license = "unspecified"; - - if (rom->rom_signature != 0xaa55) - return 0; - if (rom->vrom_signature != VMI_SIGNATURE) - return 0; - if (rom->api_version_maj != VMI_API_REV_MAJOR || - rom->api_version_min+1 < VMI_API_REV_MINOR+1) { - printk(KERN_WARNING "VMI: Found mismatched rom version %d.%d\n", - rom->api_version_maj, - rom->api_version_min); - return 0; - } - - /* - * Relying on the VMI_SIGNATURE field is not 100% safe, so check - * the PCI header and device type to make sure this is really a - * VMI device. - */ - if (!rom->pci_header_offs) { - printk(KERN_WARNING "VMI: ROM does not contain PCI header.\n"); - return 0; - } - - pci = (struct pci_header *)((char *)rom+rom->pci_header_offs); - if (pci->vendorID != PCI_VENDOR_ID_VMWARE || - pci->deviceID != PCI_DEVICE_ID_VMWARE_VMI) { - /* Allow it to run... anyways, but warn */ - printk(KERN_WARNING "VMI: ROM from unknown manufacturer\n"); - } - - if (rom->pnp_header_offs) { - pnp = (struct pnp_header *)((char *)rom+rom->pnp_header_offs); - if (pnp->manufacturer_offset) - manufacturer = (const char *)rom+pnp->manufacturer_offset; - if (pnp->product_offset) - product = (const char *)rom+pnp->product_offset; - } - - if (rom->license_offs) - license = (char *)rom+rom->license_offs; - - printk(KERN_INFO "VMI: Found %s %s, API version %d.%d, ROM version %d.%d\n", - manufacturer, product, - rom->api_version_maj, rom->api_version_min, - pci->rom_version_maj, pci->rom_version_min); - - /* Don't allow BSD/MIT here for now because we don't want to end up - with any binary only shim layers */ - if (strcmp(license, "GPL") && strcmp(license, "GPL v2")) { - printk(KERN_WARNING "VMI: Non GPL license `%s' found for ROM. Not used.\n", - license); - return 0; - } - - return 1; -} - -/* - * Probe for the VMI option ROM - */ -static inline int __init probe_vmi_rom(void) -{ - unsigned long base; - - /* VMI ROM is in option ROM area, check signature */ - for (base = 0xC0000; base < 0xE0000; base += 2048) { - struct vrom_header *romstart; - romstart = (struct vrom_header *)isa_bus_to_virt(base); - if (check_vmi_rom(romstart)) { - vmi_rom = romstart; - return 1; - } - } - return 0; -} - -/* - * VMI setup common to all processors - */ -void vmi_bringup(void) -{ - /* We must establish the lowmem mapping for MMU ops to work */ - if (vmi_ops.set_linear_mapping) - vmi_ops.set_linear_mapping(0, (void *)__PAGE_OFFSET, MAXMEM_PFN, 0); -} - -/* - * Return a pointer to a VMI function or NULL if unimplemented - */ -static void *vmi_get_function(int vmicall) -{ - u64 reloc; - const struct vmi_relocation_info *rel = (struct vmi_relocation_info *)&reloc; - reloc = call_vrom_long_func(vmi_rom, get_reloc, vmicall); - BUG_ON(rel->type == VMI_RELOCATION_JUMP_REL); - if (rel->type == VMI_RELOCATION_CALL_REL) - return (void *)rel->eip; - else - return NULL; -} - -/* - * Helper macro for making the VMI paravirt-ops fill code readable. - * For unimplemented operations, fall back to default, unless nop - * is returned by the ROM. - */ -#define para_fill(opname, vmicall) \ -do { \ - reloc = call_vrom_long_func(vmi_rom, get_reloc, \ - VMI_CALL_##vmicall); \ - if (rel->type == VMI_RELOCATION_CALL_REL) \ - opname = (void *)rel->eip; \ - else if (rel->type == VMI_RELOCATION_NOP) \ - opname = (void *)vmi_nop; \ - else if (rel->type != VMI_RELOCATION_NONE) \ - printk(KERN_WARNING "VMI: Unknown relocation " \ - "type %d for " #vmicall"\n",\ - rel->type); \ -} while (0) - -/* - * Helper macro for making the VMI paravirt-ops fill code readable. - * For cached operations which do not match the VMI ROM ABI and must - * go through a tranlation stub. Ignore NOPs, since it is not clear - * a NOP * VMI function corresponds to a NOP paravirt-op when the - * functions are not in 1-1 correspondence. - */ -#define para_wrap(opname, wrapper, cache, vmicall) \ -do { \ - reloc = call_vrom_long_func(vmi_rom, get_reloc, \ - VMI_CALL_##vmicall); \ - BUG_ON(rel->type == VMI_RELOCATION_JUMP_REL); \ - if (rel->type == VMI_RELOCATION_CALL_REL) { \ - opname = wrapper; \ - vmi_ops.cache = (void *)rel->eip; \ - } \ -} while (0) - -/* - * Activate the VMI interface and switch into paravirtualized mode - */ -static inline int __init activate_vmi(void) -{ - short kernel_cs; - u64 reloc; - const struct vmi_relocation_info *rel = (struct vmi_relocation_info *)&reloc; - - /* - * Prevent page tables from being allocated in highmem, even if - * CONFIG_HIGHPTE is enabled. - */ - __userpte_alloc_gfp &= ~__GFP_HIGHMEM; - - if (call_vrom_func(vmi_rom, vmi_init) != 0) { - printk(KERN_ERR "VMI ROM failed to initialize!"); - return 0; - } - savesegment(cs, kernel_cs); - - pv_info.paravirt_enabled = 1; - pv_info.kernel_rpl = kernel_cs & SEGMENT_RPL_MASK; - pv_info.name = "vmi [deprecated]"; - - pv_init_ops.patch = vmi_patch; - - /* - * Many of these operations are ABI compatible with VMI. - * This means we can fill in the paravirt-ops with direct - * pointers into the VMI ROM. If the calling convention for - * these operations changes, this code needs to be updated. - * - * Exceptions - * CPUID paravirt-op uses pointers, not the native ISA - * halt has no VMI equivalent; all VMI halts are "safe" - * no MSR support yet - just trap and emulate. VMI uses the - * same ABI as the native ISA, but Linux wants exceptions - * from bogus MSR read / write handled - * rdpmc is not yet used in Linux - */ - - /* CPUID is special, so very special it gets wrapped like a present */ - para_wrap(pv_cpu_ops.cpuid, vmi_cpuid, cpuid, CPUID); - - para_fill(pv_cpu_ops.clts, CLTS); - para_fill(pv_cpu_ops.get_debugreg, GetDR); - para_fill(pv_cpu_ops.set_debugreg, SetDR); - para_fill(pv_cpu_ops.read_cr0, GetCR0); - para_fill(pv_mmu_ops.read_cr2, GetCR2); - para_fill(pv_mmu_ops.read_cr3, GetCR3); - para_fill(pv_cpu_ops.read_cr4, GetCR4); - para_fill(pv_cpu_ops.write_cr0, SetCR0); - para_fill(pv_mmu_ops.write_cr2, SetCR2); - para_fill(pv_mmu_ops.write_cr3, SetCR3); - para_fill(pv_cpu_ops.write_cr4, SetCR4); - - para_fill(pv_irq_ops.save_fl.func, GetInterruptMask); - para_fill(pv_irq_ops.restore_fl.func, SetInterruptMask); - para_fill(pv_irq_ops.irq_disable.func, DisableInterrupts); - para_fill(pv_irq_ops.irq_enable.func, EnableInterrupts); - - para_fill(pv_cpu_ops.wbinvd, WBINVD); - para_fill(pv_cpu_ops.read_tsc, RDTSC); - - /* The following we emulate with trap and emulate for now */ - /* paravirt_ops.read_msr = vmi_rdmsr */ - /* paravirt_ops.write_msr = vmi_wrmsr */ - /* paravirt_ops.rdpmc = vmi_rdpmc */ - - /* TR interface doesn't pass TR value, wrap */ - para_wrap(pv_cpu_ops.load_tr_desc, vmi_set_tr, set_tr, SetTR); - - /* LDT is special, too */ - para_wrap(pv_cpu_ops.set_ldt, vmi_set_ldt, _set_ldt, SetLDT); - - para_fill(pv_cpu_ops.load_gdt, SetGDT); - para_fill(pv_cpu_ops.load_idt, SetIDT); - para_fill(pv_cpu_ops.store_gdt, GetGDT); - para_fill(pv_cpu_ops.store_idt, GetIDT); - para_fill(pv_cpu_ops.store_tr, GetTR); - pv_cpu_ops.load_tls = vmi_load_tls; - para_wrap(pv_cpu_ops.write_ldt_entry, vmi_write_ldt_entry, - write_ldt_entry, WriteLDTEntry); - para_wrap(pv_cpu_ops.write_gdt_entry, vmi_write_gdt_entry, - write_gdt_entry, WriteGDTEntry); - para_wrap(pv_cpu_ops.write_idt_entry, vmi_write_idt_entry, - write_idt_entry, WriteIDTEntry); - para_wrap(pv_cpu_ops.load_sp0, vmi_load_sp0, set_kernel_stack, UpdateKernelStack); - para_fill(pv_cpu_ops.set_iopl_mask, SetIOPLMask); - para_fill(pv_cpu_ops.io_delay, IODelay); - - para_wrap(pv_cpu_ops.start_context_switch, vmi_start_context_switch, - set_lazy_mode, SetLazyMode); - para_wrap(pv_cpu_ops.end_context_switch, vmi_end_context_switch, - set_lazy_mode, SetLazyMode); - - para_wrap(pv_mmu_ops.lazy_mode.enter, vmi_enter_lazy_mmu, - set_lazy_mode, SetLazyMode); - para_wrap(pv_mmu_ops.lazy_mode.leave, vmi_leave_lazy_mmu, - set_lazy_mode, SetLazyMode); - - /* user and kernel flush are just handled with different flags to FlushTLB */ - para_wrap(pv_mmu_ops.flush_tlb_user, vmi_flush_tlb_user, _flush_tlb, FlushTLB); - para_wrap(pv_mmu_ops.flush_tlb_kernel, vmi_flush_tlb_kernel, _flush_tlb, FlushTLB); - para_fill(pv_mmu_ops.flush_tlb_single, InvalPage); - - /* - * Until a standard flag format can be agreed on, we need to - * implement these as wrappers in Linux. Get the VMI ROM - * function pointers for the two backend calls. - */ -#ifdef CONFIG_X86_PAE - vmi_ops.set_pte = vmi_get_function(VMI_CALL_SetPxELong); - vmi_ops.update_pte = vmi_get_function(VMI_CALL_UpdatePxELong); -#else - vmi_ops.set_pte = vmi_get_function(VMI_CALL_SetPxE); - vmi_ops.update_pte = vmi_get_function(VMI_CALL_UpdatePxE); -#endif - - if (vmi_ops.set_pte) { - pv_mmu_ops.set_pte = vmi_set_pte; - pv_mmu_ops.set_pte_at = vmi_set_pte_at; - pv_mmu_ops.set_pmd = vmi_set_pmd; -#ifdef CONFIG_X86_PAE - pv_mmu_ops.set_pte_atomic = vmi_set_pte_atomic; - pv_mmu_ops.set_pud = vmi_set_pud; - pv_mmu_ops.pte_clear = vmi_pte_clear; - pv_mmu_ops.pmd_clear = vmi_pmd_clear; -#endif - } - - if (vmi_ops.update_pte) { - pv_mmu_ops.pte_update = vmi_update_pte; - pv_mmu_ops.pte_update_defer = vmi_update_pte_defer; - } - - vmi_ops.allocate_page = vmi_get_function(VMI_CALL_AllocatePage); - if (vmi_ops.allocate_page) { - pv_mmu_ops.alloc_pte = vmi_allocate_pte; - pv_mmu_ops.alloc_pmd = vmi_allocate_pmd; - pv_mmu_ops.alloc_pmd_clone = vmi_allocate_pmd_clone; - } - - vmi_ops.release_page = vmi_get_function(VMI_CALL_ReleasePage); - if (vmi_ops.release_page) { - pv_mmu_ops.release_pte = vmi_release_pte; - pv_mmu_ops.release_pmd = vmi_release_pmd; - pv_mmu_ops.pgd_free = vmi_pgd_free; - } - - /* Set linear is needed in all cases */ - vmi_ops.set_linear_mapping = vmi_get_function(VMI_CALL_SetLinearMapping); - - /* - * These MUST always be patched. Don't support indirect jumps - * through these operations, as the VMI interface may use either - * a jump or a call to get to these operations, depending on - * the backend. They are performance critical anyway, so requiring - * a patch is not a big problem. - */ - pv_cpu_ops.irq_enable_sysexit = (void *)0xfeedbab0; - pv_cpu_ops.iret = (void *)0xbadbab0; - -#ifdef CONFIG_SMP - para_wrap(pv_apic_ops.startup_ipi_hook, vmi_startup_ipi_hook, set_initial_ap_state, SetInitialAPState); -#endif - -#ifdef CONFIG_X86_LOCAL_APIC - para_fill(apic->read, APICRead); - para_fill(apic->write, APICWrite); -#endif - - /* - * Check for VMI timer functionality by probing for a cycle frequency method - */ - reloc = call_vrom_long_func(vmi_rom, get_reloc, VMI_CALL_GetCycleFrequency); - if (!disable_vmi_timer && rel->type != VMI_RELOCATION_NONE) { - vmi_timer_ops.get_cycle_frequency = (void *)rel->eip; - vmi_timer_ops.get_cycle_counter = - vmi_get_function(VMI_CALL_GetCycleCounter); - vmi_timer_ops.get_wallclock = - vmi_get_function(VMI_CALL_GetWallclockTime); - vmi_timer_ops.wallclock_updated = - vmi_get_function(VMI_CALL_WallclockUpdated); - vmi_timer_ops.set_alarm = vmi_get_function(VMI_CALL_SetAlarm); - vmi_timer_ops.cancel_alarm = - vmi_get_function(VMI_CALL_CancelAlarm); - x86_init.timers.timer_init = vmi_time_init; -#ifdef CONFIG_X86_LOCAL_APIC - x86_init.timers.setup_percpu_clockev = vmi_time_bsp_init; - x86_cpuinit.setup_percpu_clockev = vmi_time_ap_init; -#endif - pv_time_ops.sched_clock = vmi_sched_clock; - x86_platform.calibrate_tsc = vmi_tsc_khz; - x86_platform.get_wallclock = vmi_get_wallclock; - x86_platform.set_wallclock = vmi_set_wallclock; - - /* We have true wallclock functions; disable CMOS clock sync */ - no_sync_cmos_clock = 1; - } else { - disable_noidle = 1; - disable_vmi_timer = 1; - } - - para_fill(pv_irq_ops.safe_halt, Halt); - - /* - * Alternative instruction rewriting doesn't happen soon enough - * to convert VMI_IRET to a call instead of a jump; so we have - * to do this before IRQs get reenabled. Fortunately, it is - * idempotent. - */ - apply_paravirt(__parainstructions, __parainstructions_end); - - vmi_bringup(); - - return 1; -} - -#undef para_fill - -void __init vmi_init(void) -{ - if (!vmi_rom) - probe_vmi_rom(); - else - check_vmi_rom(vmi_rom); - - /* In case probing for or validating the ROM failed, basil */ - if (!vmi_rom) - return; - - reserve_top_address(-vmi_rom->virtual_top); - -#ifdef CONFIG_X86_IO_APIC - /* This is virtual hardware; timer routing is wired correctly */ - no_timer_check = 1; -#endif -} - -void __init vmi_activate(void) -{ - unsigned long flags; - - if (!vmi_rom) - return; - - local_irq_save(flags); - activate_vmi(); - local_irq_restore(flags & X86_EFLAGS_IF); -} - -static int __init parse_vmi(char *arg) -{ - if (!arg) - return -EINVAL; - - if (!strcmp(arg, "disable_pge")) { - clear_cpu_cap(&boot_cpu_data, X86_FEATURE_PGE); - disable_pge = 1; - } else if (!strcmp(arg, "disable_pse")) { - clear_cpu_cap(&boot_cpu_data, X86_FEATURE_PSE); - disable_pse = 1; - } else if (!strcmp(arg, "disable_sep")) { - clear_cpu_cap(&boot_cpu_data, X86_FEATURE_SEP); - disable_sep = 1; - } else if (!strcmp(arg, "disable_tsc")) { - clear_cpu_cap(&boot_cpu_data, X86_FEATURE_TSC); - disable_tsc = 1; - } else if (!strcmp(arg, "disable_mtrr")) { - clear_cpu_cap(&boot_cpu_data, X86_FEATURE_MTRR); - disable_mtrr = 1; - } else if (!strcmp(arg, "disable_timer")) { - disable_vmi_timer = 1; - disable_noidle = 1; - } else if (!strcmp(arg, "disable_noidle")) - disable_noidle = 1; - return 0; -} - -early_param("vmi", parse_vmi); diff --git a/arch/x86/kernel/vmiclock_32.c b/arch/x86/kernel/vmiclock_32.c deleted file mode 100644 index 5e1ff66ecd73..000000000000 --- a/arch/x86/kernel/vmiclock_32.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * VMI paravirtual timer support routines. - * - * Copyright (C) 2007, VMware, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define VMI_ONESHOT (VMI_ALARM_IS_ONESHOT | VMI_CYCLES_REAL | vmi_get_alarm_wiring()) -#define VMI_PERIODIC (VMI_ALARM_IS_PERIODIC | VMI_CYCLES_REAL | vmi_get_alarm_wiring()) - -static DEFINE_PER_CPU(struct clock_event_device, local_events); - -static inline u32 vmi_counter(u32 flags) -{ - /* Given VMI_ONESHOT or VMI_PERIODIC, return the corresponding - * cycle counter. */ - return flags & VMI_ALARM_COUNTER_MASK; -} - -/* paravirt_ops.get_wallclock = vmi_get_wallclock */ -unsigned long vmi_get_wallclock(void) -{ - unsigned long long wallclock; - wallclock = vmi_timer_ops.get_wallclock(); // nsec - (void)do_div(wallclock, 1000000000); // sec - - return wallclock; -} - -/* paravirt_ops.set_wallclock = vmi_set_wallclock */ -int vmi_set_wallclock(unsigned long now) -{ - return 0; -} - -/* paravirt_ops.sched_clock = vmi_sched_clock */ -unsigned long long vmi_sched_clock(void) -{ - return cycles_2_ns(vmi_timer_ops.get_cycle_counter(VMI_CYCLES_AVAILABLE)); -} - -/* x86_platform.calibrate_tsc = vmi_tsc_khz */ -unsigned long vmi_tsc_khz(void) -{ - unsigned long long khz; - khz = vmi_timer_ops.get_cycle_frequency(); - (void)do_div(khz, 1000); - return khz; -} - -static inline unsigned int vmi_get_timer_vector(void) -{ - return IRQ0_VECTOR; -} - -/** vmi clockchip */ -#ifdef CONFIG_X86_LOCAL_APIC -static unsigned int startup_timer_irq(unsigned int irq) -{ - unsigned long val = apic_read(APIC_LVTT); - apic_write(APIC_LVTT, vmi_get_timer_vector()); - - return (val & APIC_SEND_PENDING); -} - -static void mask_timer_irq(unsigned int irq) -{ - unsigned long val = apic_read(APIC_LVTT); - apic_write(APIC_LVTT, val | APIC_LVT_MASKED); -} - -static void unmask_timer_irq(unsigned int irq) -{ - unsigned long val = apic_read(APIC_LVTT); - apic_write(APIC_LVTT, val & ~APIC_LVT_MASKED); -} - -static void ack_timer_irq(unsigned int irq) -{ - ack_APIC_irq(); -} - -static struct irq_chip vmi_chip __read_mostly = { - .name = "VMI-LOCAL", - .startup = startup_timer_irq, - .mask = mask_timer_irq, - .unmask = unmask_timer_irq, - .ack = ack_timer_irq -}; -#endif - -/** vmi clockevent */ -#define VMI_ALARM_WIRED_IRQ0 0x00000000 -#define VMI_ALARM_WIRED_LVTT 0x00010000 -static int vmi_wiring = VMI_ALARM_WIRED_IRQ0; - -static inline int vmi_get_alarm_wiring(void) -{ - return vmi_wiring; -} - -static void vmi_timer_set_mode(enum clock_event_mode mode, - struct clock_event_device *evt) -{ - cycle_t now, cycles_per_hz; - BUG_ON(!irqs_disabled()); - - switch (mode) { - case CLOCK_EVT_MODE_ONESHOT: - case CLOCK_EVT_MODE_RESUME: - break; - case CLOCK_EVT_MODE_PERIODIC: - cycles_per_hz = vmi_timer_ops.get_cycle_frequency(); - (void)do_div(cycles_per_hz, HZ); - now = vmi_timer_ops.get_cycle_counter(vmi_counter(VMI_PERIODIC)); - vmi_timer_ops.set_alarm(VMI_PERIODIC, now, cycles_per_hz); - break; - case CLOCK_EVT_MODE_UNUSED: - case CLOCK_EVT_MODE_SHUTDOWN: - switch (evt->mode) { - case CLOCK_EVT_MODE_ONESHOT: - vmi_timer_ops.cancel_alarm(VMI_ONESHOT); - break; - case CLOCK_EVT_MODE_PERIODIC: - vmi_timer_ops.cancel_alarm(VMI_PERIODIC); - break; - default: - break; - } - break; - default: - break; - } -} - -static int vmi_timer_next_event(unsigned long delta, - struct clock_event_device *evt) -{ - /* Unfortunately, set_next_event interface only passes relative - * expiry, but we want absolute expiry. It'd be better if were - * were passed an absolute expiry, since a bunch of time may - * have been stolen between the time the delta is computed and - * when we set the alarm below. */ - cycle_t now = vmi_timer_ops.get_cycle_counter(vmi_counter(VMI_ONESHOT)); - - BUG_ON(evt->mode != CLOCK_EVT_MODE_ONESHOT); - vmi_timer_ops.set_alarm(VMI_ONESHOT, now + delta, 0); - return 0; -} - -static struct clock_event_device vmi_clockevent = { - .name = "vmi-timer", - .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .shift = 22, - .set_mode = vmi_timer_set_mode, - .set_next_event = vmi_timer_next_event, - .rating = 1000, - .irq = 0, -}; - -static irqreturn_t vmi_timer_interrupt(int irq, void *dev_id) -{ - struct clock_event_device *evt = &__get_cpu_var(local_events); - evt->event_handler(evt); - return IRQ_HANDLED; -} - -static struct irqaction vmi_clock_action = { - .name = "vmi-timer", - .handler = vmi_timer_interrupt, - .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TIMER, -}; - -static void __devinit vmi_time_init_clockevent(void) -{ - cycle_t cycles_per_msec; - struct clock_event_device *evt; - - int cpu = smp_processor_id(); - evt = &__get_cpu_var(local_events); - - /* Use cycles_per_msec since div_sc params are 32-bits. */ - cycles_per_msec = vmi_timer_ops.get_cycle_frequency(); - (void)do_div(cycles_per_msec, 1000); - - memcpy(evt, &vmi_clockevent, sizeof(*evt)); - /* Must pick .shift such that .mult fits in 32-bits. Choosing - * .shift to be 22 allows 2^(32-22) cycles per nano-seconds - * before overflow. */ - evt->mult = div_sc(cycles_per_msec, NSEC_PER_MSEC, evt->shift); - /* Upper bound is clockevent's use of ulong for cycle deltas. */ - evt->max_delta_ns = clockevent_delta2ns(ULONG_MAX, evt); - evt->min_delta_ns = clockevent_delta2ns(1, evt); - evt->cpumask = cpumask_of(cpu); - - printk(KERN_WARNING "vmi: registering clock event %s. mult=%u shift=%u\n", - evt->name, evt->mult, evt->shift); - clockevents_register_device(evt); -} - -void __init vmi_time_init(void) -{ - unsigned int cpu; - /* Disable PIT: BIOSes start PIT CH0 with 18.2hz peridic. */ - outb_pit(0x3a, PIT_MODE); /* binary, mode 5, LSB/MSB, ch 0 */ - - vmi_time_init_clockevent(); - setup_irq(0, &vmi_clock_action); - for_each_possible_cpu(cpu) - per_cpu(vector_irq, cpu)[vmi_get_timer_vector()] = 0; -} - -#ifdef CONFIG_X86_LOCAL_APIC -void __devinit vmi_time_bsp_init(void) -{ - /* - * On APIC systems, we want local timers to fire on each cpu. We do - * this by programming LVTT to deliver timer events to the IRQ handler - * for IRQ-0, since we can't re-use the APIC local timer handler - * without interfering with that code. - */ - clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL); - local_irq_disable(); -#ifdef CONFIG_SMP - /* - * XXX handle_percpu_irq only defined for SMP; we need to switch over - * to using it, since this is a local interrupt, which each CPU must - * handle individually without locking out or dropping simultaneous - * local timers on other CPUs. We also don't want to trigger the - * quirk workaround code for interrupts which gets invoked from - * handle_percpu_irq via eoi, so we use our own IRQ chip. - */ - set_irq_chip_and_handler_name(0, &vmi_chip, handle_percpu_irq, "lvtt"); -#else - set_irq_chip_and_handler_name(0, &vmi_chip, handle_edge_irq, "lvtt"); -#endif - vmi_wiring = VMI_ALARM_WIRED_LVTT; - apic_write(APIC_LVTT, vmi_get_timer_vector()); - local_irq_enable(); - clockevents_notify(CLOCK_EVT_NOTIFY_RESUME, NULL); -} - -void __devinit vmi_time_ap_init(void) -{ - vmi_time_init_clockevent(); - apic_write(APIC_LVTT, vmi_get_timer_vector()); -} -#endif - -/** vmi clocksource */ -static struct clocksource clocksource_vmi; - -static cycle_t read_real_cycles(struct clocksource *cs) -{ - cycle_t ret = (cycle_t)vmi_timer_ops.get_cycle_counter(VMI_CYCLES_REAL); - return max(ret, clocksource_vmi.cycle_last); -} - -static struct clocksource clocksource_vmi = { - .name = "vmi-timer", - .rating = 450, - .read = read_real_cycles, - .mask = CLOCKSOURCE_MASK(64), - .mult = 0, /* to be set */ - .shift = 22, - .flags = CLOCK_SOURCE_IS_CONTINUOUS, -}; - -static int __init init_vmi_clocksource(void) -{ - cycle_t cycles_per_msec; - - if (!vmi_timer_ops.get_cycle_frequency) - return 0; - /* Use khz2mult rather than hz2mult since hz arg is only 32-bits. */ - cycles_per_msec = vmi_timer_ops.get_cycle_frequency(); - (void)do_div(cycles_per_msec, 1000); - - /* Note that clocksource.{mult, shift} converts in the opposite direction - * as clockevents. */ - clocksource_vmi.mult = clocksource_khz2mult(cycles_per_msec, - clocksource_vmi.shift); - - printk(KERN_WARNING "vmi: registering clock source khz=%lld\n", cycles_per_msec); - return clocksource_register(&clocksource_vmi); - -} -module_init(init_vmi_clocksource); -- cgit v1.2.3-70-g09d2 From 2c96c7751d2bb822542b03ddfaca70933f5aaf02 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 23 Aug 2010 16:34:02 -0700 Subject: rcu: upgrade stallwarn.txt documentation for CPU-bound RT processes CPU-bound real-time processes can cause RCU CPU stall warnings, and much other trouble as well. Document the fact that they can cause RCU CPU stall warnings. Suggested-by: Darren Hart Signed-off-by: Paul E. McKenney --- Documentation/RCU/stallwarn.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'Documentation') diff --git a/Documentation/RCU/stallwarn.txt b/Documentation/RCU/stallwarn.txt index 44c6dcc93d6d..862c08ef1fde 100644 --- a/Documentation/RCU/stallwarn.txt +++ b/Documentation/RCU/stallwarn.txt @@ -80,6 +80,24 @@ o A CPU looping with bottom halves disabled. This condition can o For !CONFIG_PREEMPT kernels, a CPU looping anywhere in the kernel without invoking schedule(). +o A CPU-bound real-time task in a CONFIG_PREEMPT kernel, which might + happen to preempt a low-priority task in the middle of an RCU + read-side critical section. This is especially damaging if + that low-priority task is not permitted to run on any other CPU, + in which case the next RCU grace period can never complete, which + will eventually cause the system to run out of memory and hang. + While the system is in the process of running itself out of + memory, you might see stall-warning messages. + +o A CPU-bound real-time task in a CONFIG_PREEMPT_RT kernel that + is running at a higher priority than the RCU softirq threads. + This will prevent RCU callbacks from ever being invoked, + and in a CONFIG_TREE_PREEMPT_RCU kernel will further prevent + RCU grace periods from ever completing. Either way, the + system will eventually run out of memory and hang. In the + CONFIG_TREE_PREEMPT_RCU case, you might see stall-warning + messages. + o A bug in the RCU implementation. o A hardware failure. This is quite unlikely, but has occurred -- cgit v1.2.3-70-g09d2 From 352de5573f60af0ab054859931cfb68eecb1832b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 17 Aug 2010 12:04:34 +0200 Subject: wireless: move documentation books This moves mac80211 documentation into a new 802.11 bookset and also adds a cfg80211 book to the set. All of this is rather incomplete, but it's easier to work with big code moving as a separate patch. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- Documentation/DocBook/80211.tmpl | 371 ++++++++++++++++++++++++++++++++++++ Documentation/DocBook/Makefile | 2 +- Documentation/DocBook/mac80211.tmpl | 337 -------------------------------- 3 files changed, 372 insertions(+), 338 deletions(-) create mode 100644 Documentation/DocBook/80211.tmpl delete mode 100644 Documentation/DocBook/mac80211.tmpl (limited to 'Documentation') diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl new file mode 100644 index 000000000000..6f88e184d2e7 --- /dev/null +++ b/Documentation/DocBook/80211.tmpl @@ -0,0 +1,371 @@ + + + + + The 802.11 subsystems – for kernel developers + + Explaining wireless 802.11 networking in the Linux kernel + + + + 2007-2009 + Johannes Berg + + + + + Johannes + Berg + +
johannes@sipsolutions.net
+
+
+
+ + + + This documentation 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 documentation is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + You should have received a copy of the GNU General Public + License along with this documentation; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + For more details see the file COPYING in the source + distribution of Linux. + + + + + + These books attempt to give a description of the + various subsystems that play a role in 802.11 wireless + networking in Linux. Since these books are for kernel + developers they attempts to document the structures + and functions used in the kernel as well as giving a + higher-level overview. + + + The reader is expected to be familiar with the 802.11 + standard as published by the IEEE in 802.11-2007 (or + possibly later versions). References to this standard + will be given as "802.11-2007 8.1.5". + + +
+ + + The cfg80211 subsystem + + + +MISSING + + + + +!Finclude/net/cfg80211.h ieee80211_band + + + + + The mac80211 subsystem + +!Pinclude/net/mac80211.h Introduction +!Pinclude/net/mac80211.h Warning + + + + + + + + + The basic mac80211 driver interface + + + You should read and understand the information contained + within this part of the book while implementing a driver. + In some chapters, advanced usage is noted, that may be + skipped at first. + + + This part of the book only covers station and monitor mode + functionality, additional information required to implement + the other modes is covered in the second part of the book. + + + + + Basic hardware handling + TBD + + This chapter shall contain information on getting a hw + struct allocated and registered with mac80211. + + + Since it is required to allocate rates/modes before registering + a hw struct, this chapter shall also contain information on setting + up the rate/mode structs. + + + Additionally, some discussion about the callbacks and + the general programming model should be in here, including + the definition of ieee80211_ops which will be referred to + a lot. + + + Finally, a discussion of hardware capabilities should be done + with references to other parts of the book. + + +!Finclude/net/mac80211.h ieee80211_hw +!Finclude/net/mac80211.h ieee80211_hw_flags +!Finclude/net/mac80211.h SET_IEEE80211_DEV +!Finclude/net/mac80211.h SET_IEEE80211_PERM_ADDR +!Finclude/net/mac80211.h ieee80211_ops +!Finclude/net/mac80211.h ieee80211_alloc_hw +!Finclude/net/mac80211.h ieee80211_register_hw +!Finclude/net/mac80211.h ieee80211_get_tx_led_name +!Finclude/net/mac80211.h ieee80211_get_rx_led_name +!Finclude/net/mac80211.h ieee80211_get_assoc_led_name +!Finclude/net/mac80211.h ieee80211_get_radio_led_name +!Finclude/net/mac80211.h ieee80211_unregister_hw +!Finclude/net/mac80211.h ieee80211_free_hw + + + + PHY configuration + TBD + + This chapter should describe PHY handling including + start/stop callbacks and the various structures used. + +!Finclude/net/mac80211.h ieee80211_conf +!Finclude/net/mac80211.h ieee80211_conf_flags + + + + Virtual interfaces + TBD + + This chapter should describe virtual interface basics + that are relevant to the driver (VLANs, MGMT etc are not.) + It should explain the use of the add_iface/remove_iface + callbacks as well as the interface configuration callbacks. + + Things related to AP mode should be discussed there. + + Things related to supporting multiple interfaces should be + in the appropriate chapter, a BIG FAT note should be here about + this though and the recommendation to allow only a single + interface in STA mode at first! + +!Finclude/net/mac80211.h ieee80211_vif + + + + Receive and transmit processing + + what should be here + TBD + + This should describe the receive and transmit + paths in mac80211/the drivers as well as + transmit status handling. + + + + Frame format +!Pinclude/net/mac80211.h Frame format + + + Packet alignment +!Pnet/mac80211/rx.c Packet alignment + + + Calling into mac80211 from interrupts +!Pinclude/net/mac80211.h Calling mac80211 from interrupts + + + functions/definitions +!Finclude/net/mac80211.h ieee80211_rx_status +!Finclude/net/mac80211.h mac80211_rx_flags +!Finclude/net/mac80211.h ieee80211_tx_info +!Finclude/net/mac80211.h ieee80211_rx +!Finclude/net/mac80211.h ieee80211_rx_irqsafe +!Finclude/net/mac80211.h ieee80211_tx_status +!Finclude/net/mac80211.h ieee80211_tx_status_irqsafe +!Finclude/net/mac80211.h ieee80211_rts_get +!Finclude/net/mac80211.h ieee80211_rts_duration +!Finclude/net/mac80211.h ieee80211_ctstoself_get +!Finclude/net/mac80211.h ieee80211_ctstoself_duration +!Finclude/net/mac80211.h ieee80211_generic_frame_duration +!Finclude/net/mac80211.h ieee80211_wake_queue +!Finclude/net/mac80211.h ieee80211_stop_queue +!Finclude/net/mac80211.h ieee80211_wake_queues +!Finclude/net/mac80211.h ieee80211_stop_queues + + + + + Frame filtering +!Pinclude/net/mac80211.h Frame filtering +!Finclude/net/mac80211.h ieee80211_filter_flags + + + + + Advanced driver interface + + + Information contained within this part of the book is + of interest only for advanced interaction of mac80211 + with drivers to exploit more hardware capabilities and + improve performance. + + + + + Hardware crypto acceleration +!Pinclude/net/mac80211.h Hardware crypto acceleration + +!Finclude/net/mac80211.h set_key_cmd +!Finclude/net/mac80211.h ieee80211_key_conf +!Finclude/net/mac80211.h ieee80211_key_alg +!Finclude/net/mac80211.h ieee80211_key_flags + + + + Powersave support +!Pinclude/net/mac80211.h Powersave support + + + + Beacon filter support +!Pinclude/net/mac80211.h Beacon filter support +!Finclude/net/mac80211.h ieee80211_beacon_loss + + + + Multiple queues and QoS support + TBD +!Finclude/net/mac80211.h ieee80211_tx_queue_params + + + + Access point mode support + TBD + Some parts of the if_conf should be discussed here instead + + Insert notes about VLAN interfaces with hw crypto here or + in the hw crypto chapter. + +!Finclude/net/mac80211.h ieee80211_get_buffered_bc +!Finclude/net/mac80211.h ieee80211_beacon_get + + + + Supporting multiple virtual interfaces + TBD + + Note: WDS with identical MAC address should almost always be OK + + + Insert notes about having multiple virtual interfaces with + different MAC addresses here, note which configurations are + supported by mac80211, add notes about supporting hw crypto + with it. + + + + + Hardware scan offload + TBD +!Finclude/net/mac80211.h ieee80211_scan_completed + + + + + Rate control interface + + TBD + + This part of the book describes the rate control algorithm + interface and how it relates to mac80211 and drivers. + + + + dummy chapter + TBD + + + + + Internals + + TBD + + This part of the book describes mac80211 internals. + + + + + Key handling + + Key handling basics +!Pnet/mac80211/key.c Key handling basics + + + MORE TBD + TBD + + + + + Receive processing + TBD + + + + Transmit processing + TBD + + + + Station info handling + + Programming information +!Fnet/mac80211/sta_info.h sta_info +!Fnet/mac80211/sta_info.h ieee80211_sta_info_flags + + + STA information lifetime rules +!Pnet/mac80211/sta_info.c STA information lifetime rules + + + + + Synchronisation + TBD + Locking, lots of RCU + + + +
diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index c7e5dc7e8cb3..b6f2ba2f36b7 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -12,7 +12,7 @@ DOCBOOKS := z8530book.xml mcabook.xml device-drivers.xml \ kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \ gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \ genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \ - mac80211.xml debugobjects.xml sh.xml regulator.xml \ + 80211.xml debugobjects.xml sh.xml regulator.xml \ alsa-driver-api.xml writing-an-alsa-driver.xml \ tracepoint.xml media.xml drm.xml diff --git a/Documentation/DocBook/mac80211.tmpl b/Documentation/DocBook/mac80211.tmpl deleted file mode 100644 index affb15a344a1..000000000000 --- a/Documentation/DocBook/mac80211.tmpl +++ /dev/null @@ -1,337 +0,0 @@ - - - - - - The mac80211 subsystem for kernel developers - - - - Johannes - Berg - -
johannes@sipsolutions.net
-
-
-
- - - 2007-2009 - Johannes Berg - - - - - This documentation 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 documentation is distributed in the hope that it will be - useful, but WITHOUT ANY WARRANTY; without even the implied - warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - - - You should have received a copy of the GNU General Public - License along with this documentation; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, - MA 02111-1307 USA - - - - For more details see the file COPYING in the source - distribution of Linux. - - - - -!Pinclude/net/mac80211.h Introduction -!Pinclude/net/mac80211.h Warning - -
- - - - - - - The basic mac80211 driver interface - - - You should read and understand the information contained - within this part of the book while implementing a driver. - In some chapters, advanced usage is noted, that may be - skipped at first. - - - This part of the book only covers station and monitor mode - functionality, additional information required to implement - the other modes is covered in the second part of the book. - - - - - Basic hardware handling - TBD - - This chapter shall contain information on getting a hw - struct allocated and registered with mac80211. - - - Since it is required to allocate rates/modes before registering - a hw struct, this chapter shall also contain information on setting - up the rate/mode structs. - - - Additionally, some discussion about the callbacks and - the general programming model should be in here, including - the definition of ieee80211_ops which will be referred to - a lot. - - - Finally, a discussion of hardware capabilities should be done - with references to other parts of the book. - - -!Finclude/net/mac80211.h ieee80211_hw -!Finclude/net/mac80211.h ieee80211_hw_flags -!Finclude/net/mac80211.h SET_IEEE80211_DEV -!Finclude/net/mac80211.h SET_IEEE80211_PERM_ADDR -!Finclude/net/mac80211.h ieee80211_ops -!Finclude/net/mac80211.h ieee80211_alloc_hw -!Finclude/net/mac80211.h ieee80211_register_hw -!Finclude/net/mac80211.h ieee80211_get_tx_led_name -!Finclude/net/mac80211.h ieee80211_get_rx_led_name -!Finclude/net/mac80211.h ieee80211_get_assoc_led_name -!Finclude/net/mac80211.h ieee80211_get_radio_led_name -!Finclude/net/mac80211.h ieee80211_unregister_hw -!Finclude/net/mac80211.h ieee80211_free_hw - - - - PHY configuration - TBD - - This chapter should describe PHY handling including - start/stop callbacks and the various structures used. - -!Finclude/net/mac80211.h ieee80211_conf -!Finclude/net/mac80211.h ieee80211_conf_flags - - - - Virtual interfaces - TBD - - This chapter should describe virtual interface basics - that are relevant to the driver (VLANs, MGMT etc are not.) - It should explain the use of the add_iface/remove_iface - callbacks as well as the interface configuration callbacks. - - Things related to AP mode should be discussed there. - - Things related to supporting multiple interfaces should be - in the appropriate chapter, a BIG FAT note should be here about - this though and the recommendation to allow only a single - interface in STA mode at first! - -!Finclude/net/mac80211.h ieee80211_vif - - - - Receive and transmit processing - - what should be here - TBD - - This should describe the receive and transmit - paths in mac80211/the drivers as well as - transmit status handling. - - - - Frame format -!Pinclude/net/mac80211.h Frame format - - - Packet alignment -!Pnet/mac80211/rx.c Packet alignment - - - Calling into mac80211 from interrupts -!Pinclude/net/mac80211.h Calling mac80211 from interrupts - - - functions/definitions -!Finclude/net/mac80211.h ieee80211_rx_status -!Finclude/net/mac80211.h mac80211_rx_flags -!Finclude/net/mac80211.h ieee80211_tx_info -!Finclude/net/mac80211.h ieee80211_rx -!Finclude/net/mac80211.h ieee80211_rx_irqsafe -!Finclude/net/mac80211.h ieee80211_tx_status -!Finclude/net/mac80211.h ieee80211_tx_status_irqsafe -!Finclude/net/mac80211.h ieee80211_rts_get -!Finclude/net/mac80211.h ieee80211_rts_duration -!Finclude/net/mac80211.h ieee80211_ctstoself_get -!Finclude/net/mac80211.h ieee80211_ctstoself_duration -!Finclude/net/mac80211.h ieee80211_generic_frame_duration -!Finclude/net/mac80211.h ieee80211_wake_queue -!Finclude/net/mac80211.h ieee80211_stop_queue -!Finclude/net/mac80211.h ieee80211_wake_queues -!Finclude/net/mac80211.h ieee80211_stop_queues - - - - - Frame filtering -!Pinclude/net/mac80211.h Frame filtering -!Finclude/net/mac80211.h ieee80211_filter_flags - - - - - Advanced driver interface - - - Information contained within this part of the book is - of interest only for advanced interaction of mac80211 - with drivers to exploit more hardware capabilities and - improve performance. - - - - - Hardware crypto acceleration -!Pinclude/net/mac80211.h Hardware crypto acceleration - -!Finclude/net/mac80211.h set_key_cmd -!Finclude/net/mac80211.h ieee80211_key_conf -!Finclude/net/mac80211.h ieee80211_key_alg -!Finclude/net/mac80211.h ieee80211_key_flags - - - - Powersave support -!Pinclude/net/mac80211.h Powersave support - - - - Beacon filter support -!Pinclude/net/mac80211.h Beacon filter support -!Finclude/net/mac80211.h ieee80211_beacon_loss - - - - Multiple queues and QoS support - TBD -!Finclude/net/mac80211.h ieee80211_tx_queue_params - - - - Access point mode support - TBD - Some parts of the if_conf should be discussed here instead - - Insert notes about VLAN interfaces with hw crypto here or - in the hw crypto chapter. - -!Finclude/net/mac80211.h ieee80211_get_buffered_bc -!Finclude/net/mac80211.h ieee80211_beacon_get - - - - Supporting multiple virtual interfaces - TBD - - Note: WDS with identical MAC address should almost always be OK - - - Insert notes about having multiple virtual interfaces with - different MAC addresses here, note which configurations are - supported by mac80211, add notes about supporting hw crypto - with it. - - - - - Hardware scan offload - TBD -!Finclude/net/mac80211.h ieee80211_scan_completed - - - - - Rate control interface - - TBD - - This part of the book describes the rate control algorithm - interface and how it relates to mac80211 and drivers. - - - - dummy chapter - TBD - - - - - Internals - - TBD - - This part of the book describes mac80211 internals. - - - - - Key handling - - Key handling basics -!Pnet/mac80211/key.c Key handling basics - - - MORE TBD - TBD - - - - - Receive processing - TBD - - - - Transmit processing - TBD - - - - Station info handling - - Programming information -!Fnet/mac80211/sta_info.h sta_info -!Fnet/mac80211/sta_info.h ieee80211_sta_info_flags - - - STA information lifetime rules -!Pnet/mac80211/sta_info.c STA information lifetime rules - - - - - Synchronisation - TBD - Locking, lots of RCU - - -
-- cgit v1.2.3-70-g09d2 From 633dd1ea683d907af944bcd9814092efe9869b05 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Aug 2010 15:01:23 +0200 Subject: mac80211: fix docbook Fix a small problem in the documentation for ieee80211_request_smps, and a now erroneous inclusion of enum ieee80211_key_alg, which no longer exists after the change to ciphers. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- Documentation/DocBook/80211.tmpl | 1 - include/net/mac80211.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index 6f88e184d2e7..52310ac892ea 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -248,7 +248,6 @@ MISSING !Finclude/net/mac80211.h set_key_cmd !Finclude/net/mac80211.h ieee80211_key_conf -!Finclude/net/mac80211.h ieee80211_key_alg !Finclude/net/mac80211.h ieee80211_key_flags diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 914c747ac3a0..2a1811366076 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2537,7 +2537,7 @@ void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success); /** * ieee80211_request_smps - request SM PS transition * @vif: &struct ieee80211_vif pointer from the add_interface callback. - * @mode: new SM PS mode + * @smps_mode: new SM PS mode * * This allows the driver to request an SM PS transition in managed * mode. This is useful when the driver has more information than -- cgit v1.2.3-70-g09d2 From d70e96932de55fb2c05b1c0af1dff178651a9b77 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 19 Aug 2010 16:11:27 +0200 Subject: cfg80211: add some documentation Add some documentation for cfg80211. I'm hoping some of the regulatory documentation will be filled by somebody more familiar with it, hint hint! :) Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- Documentation/DocBook/80211.tmpl | 136 +++++++++++++++++++++++++++++++++++++-- include/net/cfg80211.h | 121 ++++++++++++++++++++++++++++++++-- 2 files changed, 246 insertions(+), 11 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index 52310ac892ea..b84c9282828f 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -65,18 +65,144 @@ +!Ainclude/net/cfg80211.h The cfg80211 subsystem - -MISSING - +!Pinclude/net/cfg80211.h Introduction - + + Device registration +!Pinclude/net/cfg80211.h Device registration !Finclude/net/cfg80211.h ieee80211_band - +!Finclude/net/cfg80211.h ieee80211_channel_flags +!Finclude/net/cfg80211.h ieee80211_channel +!Finclude/net/cfg80211.h ieee80211_rate_flags +!Finclude/net/cfg80211.h ieee80211_rate +!Finclude/net/cfg80211.h ieee80211_sta_ht_cap +!Finclude/net/cfg80211.h ieee80211_supported_band +!Finclude/net/cfg80211.h cfg80211_signal_type +!Finclude/net/cfg80211.h wiphy_params_flags +!Finclude/net/cfg80211.h wiphy_flags +!Finclude/net/cfg80211.h wiphy +!Finclude/net/cfg80211.h wireless_dev +!Finclude/net/cfg80211.h wiphy_new +!Finclude/net/cfg80211.h wiphy_register +!Finclude/net/cfg80211.h wiphy_unregister +!Finclude/net/cfg80211.h wiphy_free + +!Finclude/net/cfg80211.h wiphy_name +!Finclude/net/cfg80211.h wiphy_dev +!Finclude/net/cfg80211.h wiphy_priv +!Finclude/net/cfg80211.h priv_to_wiphy +!Finclude/net/cfg80211.h set_wiphy_dev +!Finclude/net/cfg80211.h wdev_priv + + + Actions and configuration +!Pinclude/net/cfg80211.h Actions and configuration +!Finclude/net/cfg80211.h cfg80211_ops +!Finclude/net/cfg80211.h vif_params +!Finclude/net/cfg80211.h key_params +!Finclude/net/cfg80211.h survey_info_flags +!Finclude/net/cfg80211.h survey_info +!Finclude/net/cfg80211.h beacon_parameters +!Finclude/net/cfg80211.h plink_actions +!Finclude/net/cfg80211.h station_parameters +!Finclude/net/cfg80211.h station_info_flags +!Finclude/net/cfg80211.h rate_info_flags +!Finclude/net/cfg80211.h rate_info +!Finclude/net/cfg80211.h station_info +!Finclude/net/cfg80211.h monitor_flags +!Finclude/net/cfg80211.h mpath_info_flags +!Finclude/net/cfg80211.h mpath_info +!Finclude/net/cfg80211.h bss_parameters +!Finclude/net/cfg80211.h ieee80211_txq_params +!Finclude/net/cfg80211.h cfg80211_crypto_settings +!Finclude/net/cfg80211.h cfg80211_auth_request +!Finclude/net/cfg80211.h cfg80211_assoc_request +!Finclude/net/cfg80211.h cfg80211_deauth_request +!Finclude/net/cfg80211.h cfg80211_disassoc_request +!Finclude/net/cfg80211.h cfg80211_ibss_params +!Finclude/net/cfg80211.h cfg80211_connect_params +!Finclude/net/cfg80211.h cfg80211_pmksa +!Finclude/net/cfg80211.h cfg80211_send_rx_auth +!Finclude/net/cfg80211.h cfg80211_send_auth_timeout +!Finclude/net/cfg80211.h __cfg80211_auth_canceled +!Finclude/net/cfg80211.h cfg80211_send_rx_assoc +!Finclude/net/cfg80211.h cfg80211_send_assoc_timeout +!Finclude/net/cfg80211.h cfg80211_send_deauth +!Finclude/net/cfg80211.h __cfg80211_send_deauth +!Finclude/net/cfg80211.h cfg80211_send_disassoc +!Finclude/net/cfg80211.h __cfg80211_send_disassoc +!Finclude/net/cfg80211.h cfg80211_ibss_joined +!Finclude/net/cfg80211.h cfg80211_connect_result +!Finclude/net/cfg80211.h cfg80211_roamed +!Finclude/net/cfg80211.h cfg80211_disconnected +!Finclude/net/cfg80211.h cfg80211_ready_on_channel +!Finclude/net/cfg80211.h cfg80211_remain_on_channel_expired +!Finclude/net/cfg80211.h cfg80211_new_sta +!Finclude/net/cfg80211.h cfg80211_rx_mgmt +!Finclude/net/cfg80211.h cfg80211_mgmt_tx_status +!Finclude/net/cfg80211.h cfg80211_cqm_rssi_notify +!Finclude/net/cfg80211.h cfg80211_michael_mic_failure + + + Scanning and BSS list handling +!Pinclude/net/cfg80211.h Scanning and BSS list handling +!Finclude/net/cfg80211.h cfg80211_ssid +!Finclude/net/cfg80211.h cfg80211_scan_request +!Finclude/net/cfg80211.h cfg80211_scan_done +!Finclude/net/cfg80211.h cfg80211_bss +!Finclude/net/cfg80211.h cfg80211_inform_bss_frame +!Finclude/net/cfg80211.h cfg80211_inform_bss +!Finclude/net/cfg80211.h cfg80211_unlink_bss +!Finclude/net/cfg80211.h cfg80211_find_ie +!Finclude/net/cfg80211.h ieee80211_bss_get_ie + + + Utility functions +!Pinclude/net/cfg80211.h Utility functions +!Finclude/net/cfg80211.h ieee80211_channel_to_frequency +!Finclude/net/cfg80211.h ieee80211_frequency_to_channel +!Finclude/net/cfg80211.h ieee80211_get_channel +!Finclude/net/cfg80211.h ieee80211_get_response_rate +!Finclude/net/cfg80211.h ieee80211_hdrlen +!Finclude/net/cfg80211.h ieee80211_get_hdrlen_from_skb +!Finclude/net/cfg80211.h ieee80211_radiotap_iterator + + + Data path helpers +!Pinclude/net/cfg80211.h Data path helpers +!Finclude/net/cfg80211.h ieee80211_data_to_8023 +!Finclude/net/cfg80211.h ieee80211_data_from_8023 +!Finclude/net/cfg80211.h ieee80211_amsdu_to_8023s +!Finclude/net/cfg80211.h cfg80211_classify8021d + + + Regulatory enforcement infrastructure +!Pinclude/net/cfg80211.h Regulatory enforcement infrastructure +!Finclude/net/cfg80211.h regulatory_hint +!Finclude/net/cfg80211.h wiphy_apply_custom_regulatory +!Finclude/net/cfg80211.h freq_reg_info + + + RFkill integration +!Pinclude/net/cfg80211.h RFkill integration +!Finclude/net/cfg80211.h wiphy_rfkill_set_hw_state +!Finclude/net/cfg80211.h wiphy_rfkill_start_polling +!Finclude/net/cfg80211.h wiphy_rfkill_stop_polling + + + Test mode +!Pinclude/net/cfg80211.h Test mode +!Finclude/net/cfg80211.h cfg80211_testmode_alloc_reply_skb +!Finclude/net/cfg80211.h cfg80211_testmode_reply +!Finclude/net/cfg80211.h cfg80211_testmode_alloc_event_skb +!Finclude/net/cfg80211.h cfg80211_testmode_event + diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 6a98b1b3bfde..f2740537b5d6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -25,6 +25,43 @@ #include +/** + * DOC: Introduction + * + * cfg80211 is the configuration API for 802.11 devices in Linux. It bridges + * userspace and drivers, and offers some utility functionality associated + * with 802.11. cfg80211 must, directly or indirectly via mac80211, be used + * by all modern wireless drivers in Linux, so that they offer a consistent + * API through nl80211. For backward compatibility, cfg80211 also offers + * wireless extensions to userspace, but hides them from drivers completely. + * + * Additionally, cfg80211 contains code to help enforce regulatory spectrum + * use restrictions. + */ + + +/** + * DOC: Device registration + * + * In order for a driver to use cfg80211, it must register the hardware device + * with cfg80211. This happens through a number of hardware capability structs + * described below. + * + * The fundamental structure for each device is the 'wiphy', of which each + * instance describes a physical wireless device connected to the system. Each + * such wiphy can have zero, one, or many virtual interfaces associated with + * it, which need to be identified as such by pointing the network interface's + * @ieee80211_ptr pointer to a &struct wireless_dev which further describes + * the wireless part of the interface, normally this struct is embedded in the + * network interface's private data area. Drivers can optionally allow creating + * or destroying virtual interfaces on the fly, but without at least one or the + * ability to create some the wireless device isn't useful. + * + * Each wiphy structure contains device capability information, and also has + * a pointer to the various operations the driver offers. The definitions and + * structures here describe these capabilities in detail. + */ + /* * wireless hardware capability structures */ @@ -204,6 +241,21 @@ struct ieee80211_supported_band { * Wireless hardware/device configuration structures and methods */ +/** + * DOC: Actions and configuration + * + * Each wireless device and each virtual interface offer a set of configuration + * operations and other actions that are invoked by userspace. Each of these + * actions is described in the operations structure, and the parameters these + * operations use are described separately. + * + * Additionally, some operations are asynchronous and expect to get status + * information via some functions that drivers need to call. + * + * Scanning and BSS list handling with its associated functionality is described + * in a separate chapter. + */ + /** * struct vif_params - describes virtual interface parameters * @mesh_id: mesh ID to use @@ -570,8 +622,28 @@ struct ieee80211_txq_params { /* from net/wireless.h */ struct wiphy; -/* from net/ieee80211.h */ -struct ieee80211_channel; +/** + * DOC: Scanning and BSS list handling + * + * The scanning process itself is fairly simple, but cfg80211 offers quite + * a bit of helper functionality. To start a scan, the scan operation will + * be invoked with a scan definition. This scan definition contains the + * channels to scan, and the SSIDs to send probe requests for (including the + * wildcard, if desired). A passive scan is indicated by having no SSIDs to + * probe. Additionally, a scan request may contain extra information elements + * that should be added to the probe request. The IEs are guaranteed to be + * well-formed, and will not exceed the maximum length the driver advertised + * in the wiphy structure. + * + * When scanning finds a BSS, cfg80211 needs to be notified of that, because + * it is responsible for maintaining the BSS list; the driver should not + * maintain a list itself. For this notification, various functions exist. + * + * Since drivers do not maintain a BSS list, there are also a number of + * functions to search for a BSS and obtain information about it from the + * BSS structure cfg80211 maintains. The BSS list is also made available + * to userspace. + */ /** * struct cfg80211_ssid - SSID description @@ -1574,8 +1646,10 @@ static inline void *wdev_priv(struct wireless_dev *wdev) return wiphy_priv(wdev->wiphy); } -/* - * Utility functions +/** + * DOC: Utility functions + * + * cfg80211 offers a number of utility functions that can be useful. */ /** @@ -1728,6 +1802,14 @@ unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb); */ unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc); +/** + * DOC: Data path helpers + * + * In addition to generic utilities, cfg80211 also offers + * functions that help implement the data path for devices + * that do not do the 802.11/802.3 conversion on the device. + */ + /** * ieee80211_data_to_8023 - convert an 802.11 data frame to 802.3 * @skb: the 802.11 data frame @@ -1788,8 +1870,10 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb); */ const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len); -/* - * Regulatory helper functions for wiphys +/** + * DOC: Regulatory enforcement infrastructure + * + * TODO */ /** @@ -2191,6 +2275,20 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, */ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp); +/** + * DOC: RFkill integration + * + * RFkill integration in cfg80211 is almost invisible to drivers, + * as cfg80211 automatically registers an rfkill instance for each + * wireless device it knows about. Soft kill is also translated + * into disconnecting and turning all interfaces off, drivers are + * expected to turn off the device when all interfaces are down. + * + * However, devices may have a hard RFkill line, in which case they + * also need to interact with the rfkill subsystem, via cfg80211. + * They can do this with a few helper functions documented here. + */ + /** * wiphy_rfkill_set_hw_state - notify cfg80211 about hw block state * @wiphy: the wiphy @@ -2211,6 +2309,17 @@ void wiphy_rfkill_start_polling(struct wiphy *wiphy); void wiphy_rfkill_stop_polling(struct wiphy *wiphy); #ifdef CONFIG_NL80211_TESTMODE +/** + * DOC: Test mode + * + * Test mode is a set of utility functions to allow drivers to + * interact with driver-specific tools to aid, for instance, + * factory programming. + * + * This chapter describes how drivers interact with it, for more + * information see the nl80211 book's chapter on it. + */ + /** * cfg80211_testmode_alloc_reply_skb - allocate testmode reply * @wiphy: the wiphy -- cgit v1.2.3-70-g09d2 From 9ea77bdb39b62c9bf9fd3cdd1c25a9420bccd380 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 25 Aug 2010 16:38:20 -0700 Subject: x86, bios: Make the x86 early memory reservation a kernel option Add a kernel command-line option so the x86 early memory reservation size can be adjusted at runtime instead of only at compile time. Suggested-by: Andrew Morton LKML-Reference: Signed-off-by: H. Peter Anvin --- Documentation/kernel-parameters.txt | 5 +++++ arch/x86/Kconfig | 2 +- arch/x86/kernel/setup.c | 28 ++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 2c85c0692b01..41ce93e71ded 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2150,6 +2150,11 @@ and is between 256 and 4096 characters. It is defined in the file Reserves a hole at the top of the kernel virtual address space. + reservelow= [X86] + Format: nn[K] + Set the amount of memory to reserve for BIOS at + the bottom of the address space. + reset_devices [KNL] Force drivers to reset the underlying device during initialization. diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 683ae8f9bd0b..d3590008c5dc 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1326,7 +1326,7 @@ config X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK Set whether the default state of memory_corruption_check is on or off. -config X86_LOW_RESERVE +config X86_RESERVE_LOW int "Amount of low memory, in kilobytes, to reserve for the BIOS" default 64 range 4 640 diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index eb87f1c83f91..af277e369def 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -618,6 +618,8 @@ static __init void reserve_ibft_region(void) reserve_early_overlap_ok(addr, addr + size, "ibft"); } +static unsigned reserve_low = CONFIG_X86_RESERVE_LOW << 10; + static void __init trim_bios_range(void) { /* @@ -627,9 +629,9 @@ static void __init trim_bios_range(void) * * This typically reserves additional memory (64KiB by default) * since some BIOSes are known to corrupt low memory. See the - * Kconfig help text for X86_LOW_RESERVE. + * Kconfig help text for X86_RESERVE_LOW. */ - e820_update_range(0, ALIGN(CONFIG_X86_LOW_RESERVE << 10, PAGE_SIZE), + e820_update_range(0, ALIGN(reserve_low, PAGE_SIZE), E820_RAM, E820_RESERVED); /* @@ -641,6 +643,28 @@ static void __init trim_bios_range(void) sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map); } +static int __init parse_reservelow(char *p) +{ + unsigned long long size; + + if (!p) + return -EINVAL; + + size = memparse(p, &p); + + if (size < 4096) + size = 4096; + + if (size > 640*1024) + size = 640*1024; + + reserve_low = size; + + return 0; +} + +early_param("reservelow", parse_reservelow); + /* * Determine if we were loaded by an EFI loader. If so, then we have also been * passed the efi memmap, systab, etc., so we should use these data structures -- cgit v1.2.3-70-g09d2 From b49d09c5bf9a3261ae711ac3bb91e22c172db428 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 27 Aug 2010 11:29:24 +0200 Subject: cfg80211 docbook: remove erronous !A My previous patch erroneously included an !A line (for some checking I am working on) that isn't yet supported by the docbook tools, so remove it. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- Documentation/DocBook/80211.tmpl | 1 - 1 file changed, 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index b84c9282828f..19a1210c2530 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -65,7 +65,6 @@ -!Ainclude/net/cfg80211.h The cfg80211 subsystem -- cgit v1.2.3-70-g09d2 From 29cf28ae8dc244f89e213dc198f2286659d521b5 Mon Sep 17 00:00:00 2001 From: Rafi Rubin Date: Thu, 26 Aug 2010 00:54:54 -0400 Subject: HID: ntrig: add documention The doctumentation includes a brief introduction to the driver and explanations of the filtering parameters as well as a discussion of the need for and working of the filters. Signed-off-by: Rafi Rubin Signed-off-by: Jiri Kosina --- Documentation/input/ntrig.txt | 126 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 Documentation/input/ntrig.txt (limited to 'Documentation') diff --git a/Documentation/input/ntrig.txt b/Documentation/input/ntrig.txt new file mode 100644 index 000000000000..be1fd981f73f --- /dev/null +++ b/Documentation/input/ntrig.txt @@ -0,0 +1,126 @@ +N-Trig touchscreen Driver +------------------------- + Copyright (c) 2008-2010 Rafi Rubin + Copyright (c) 2009-2010 Stephane Chatty + +This driver provides support for N-Trig pen and multi-touch sensors. Single +and multi-touch events are translated to the appropriate protocols for +the hid and input systems. Pen events are sufficiently hid compliant and +are left to the hid core. The driver also provides additional filtering +and utility functions accessible with sysfs and module parameters. + +This driver has been reported to work properly with multiple N-Trig devices +attached. + + +Parameters +---------- + +Note: values set at load time are global and will apply to all applicable +devices. Adjusting parameters with sysfs will override the load time values, +but only for that one device. + +The following parameters are used to configure filters to reduce noise: + +activate_slack number of fingers to ignore before processing events + +activation_height size threshold to activate immediately +activation_width + +min_height size threshold bellow which fingers are ignored +min_width both to decide activation and during activity + +deactivate_slack the number of "no contact" frames to ignore before + propagating the end of activity events + +When the last finger is removed from the device, it sends a number of empty +frames. By holding off on deactivation for a few frames we can tolerate false +erroneous disconnects, where the sensor may mistakenly not detect a finger that +is still present. Thus deactivate_slack addresses problems where a users might +see breaks in lines during drawing, or drop an object during a long drag. + + +Additional sysfs items +---------------------- + +These nodes just provide easy access to the ranges reported by the device. +sensor_logical_height the range for positions reported during activity +sensor_logical_width + +sensor_physical_height internal ranges not used for normal events but +sensor_physical_width useful for tuning + +All N-Trig devices with product id of 1 report events in the ranges of +X: 0-9600 +Y: 0-7200 +However not all of these devices have the same physical dimensions. Most +seem to be 12" sensors (Dell Latitude XT and XT2 and the HP TX2), and +at least one model (Dell Studio 17) has a 17" sensor. The ratio of physical +to logical sizes is used to adjust the size based filter parameters. + + +Filtering +--------- + +With the release of the early multi-touch firmwares it became increasingly +obvious that these sensors were prone to erroneous events. Users reported +seeing both inappropriately dropped contact and ghosts, contacts reported +where no finger was actually touching the screen. + +Deactivation slack helps prevent dropped contact for single touch use, but does +not address the problem of dropping one of more contacts while other contacts +are still active. Drops in the multi-touch context require additional +processing and should be handled in tandem with tacking. + +As observed ghost contacts are similar to actual use of the sensor, but they +seem to have different profiles. Ghost activity typically shows up as small +short lived touches. As such, I assume that the longer the continuous stream +of events the more likely those events are from a real contact, and that the +larger the size of each contact the more likely it is real. Balancing the +goals of preventing ghosts and accepting real events quickly (to minimize +user observable latency), the filter accumulates confidence for incoming +events until it hits thresholds and begins propagating. In the interest in +minimizing stored state as well as the cost of operations to make a decision, +I've kept that decision simple. + +Time is measured in terms of the number of fingers reported, not frames since +the probability of multiple simultaneous ghosts is expected to drop off +dramatically with increasing numbers. Rather than accumulate weight as a +function of size, I just use it as a binary threshold. A sufficiently large +contact immediately overrides the waiting period and leads to activation. + +Setting the activation size thresholds to large values will result in deciding +primarily on activation slack. If you see longer lived ghosts, turning up the +activation slack while reducing the size thresholds may suffice to eliminate +the ghosts while keeping the screen quite responsive to firm taps. + +Contacts continue to be filtered with min_height and min_width even after +the initial activation filter is satisfied. The intent is to provide +a mechanism for filtering out ghosts in the form of an extra finger while +you actually are using the screen. In practice this sort of ghost has +been far less problematic or relatively rare and I've left the defaults +set to 0 for both parameters, effectively turning off that filter. + +I don't know what the optimal values are for these filters. If the defaults +don't work for you, please play with the parameters. If you do find other +values more comfortable, I would appreciate feedback. + +The calibration of these devices does drift over time. If ghosts or contact +dropping worsen and interfere with the normal usage of your device, try +recalibrating it. + + +Calibration +----------- + +The N-Trig windows tools provide calibration and testing routines. Also an +unofficial unsupported set of user space tools including a calibrator is +available at: +http://code.launchpad.net/~rafi-seas/+junk/ntrig_calib + + +Tracking +-------- + +As of yet, all tested N-Trig firmwares do not track fingers. When multiple +contacts are active they seem to be sorted primarily by Y position. -- cgit v1.2.3-70-g09d2 From 4886fcad6e12572afbd230dfab1b268eace20d6d Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Sun, 29 Aug 2010 19:23:13 +0000 Subject: dccp ccid-2: Share TCP's minimum RTO code Using a fixed RTO_MIN of 0.2 seconds was found to cause problems for CCID-2 over 802.11g: at least once per session there was a spurious timeout. It helped to then increase the the value of RTO_MIN over this link. Since the problem is the same as in TCP, this patch makes the solution from commit "05bb1fad1cde025a864a90cfeb98dcbefe78a44a" "[TCP]: Allow minimum RTO to be configurable via routing metrics." available to DCCP. This avoids reinventing the wheel, so that e.g. the following works in the expected way now also for CCID-2: > ip route change 10.0.0.2 rto_min 800 dev ath0 Luckily this useful rto_min function was recently moved to net/tcp.h, which simplifies sharing code originating from TCP. Documentation also updated (plus minor whitespace fixes). Signed-off-by: Gerrit Renker Signed-off-by: David S. Miller --- Documentation/networking/dccp.txt | 26 ++++++++++++++++++++------ net/dccp/ccids/ccid2.c | 5 +++-- 2 files changed, 23 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index a62fdf7a6bff..cdb64922ba10 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -1,18 +1,20 @@ DCCP protocol -============ +============= Contents ======== - - Introduction - Missing features - Socket options +- Sysctl variables +- IOCTLs +- Other tunables - Notes + Introduction ============ - Datagram Congestion Control Protocol (DCCP) is an unreliable, connection oriented protocol designed to solve issues present in UDP and TCP, particularly for real-time and multimedia (streaming) traffic. @@ -29,9 +31,9 @@ It has a base protocol and pluggable congestion control IDs (CCIDs). DCCP is a Proposed Standard (RFC 2026), and the homepage for DCCP as a protocol is at http://www.ietf.org/html.charters/dccp-charter.html + Missing features ================ - The Linux DCCP implementation does not currently support all the features that are specified in RFCs 4340...42. @@ -45,7 +47,6 @@ http://linux-net.osdl.org/index.php/DCCP_Testing#Experimental_DCCP_source_tree Socket options ============== - DCCP_SOCKOPT_SERVICE sets the service. The specification mandates use of service codes (RFC 4340, sec. 8.1.2); if this socket option is not set, the socket will fall back to 0 (which means that no meaningful service code @@ -112,6 +113,7 @@ DCCP_SOCKOPT_CCID_TX_INFO On unidirectional connections it is useful to close the unused half-connection via shutdown (SHUT_WR or SHUT_RD): this will reduce per-packet processing costs. + Sysctl variables ================ Several DCCP default parameters can be managed by the following sysctls @@ -155,15 +157,27 @@ sync_ratelimit = 125 ms sequence-invalid packets on the same socket (RFC 4340, 7.5.4). The unit of this parameter is milliseconds; a value of 0 disables rate-limiting. + IOCTLS ====== FIONREAD Works as in udp(7): returns in the `int' argument pointer the size of the next pending datagram in bytes, or 0 when no datagram is pending. + +Other tunables +============== +Per-route rto_min support + CCID-2 supports the RTAX_RTO_MIN per-route setting for the minimum value + of the RTO timer. This setting can be modified via the 'rto_min' option + of iproute2; for example: + > ip route change 10.0.0.0/24 rto_min 250j dev wlan0 + > ip route add 10.0.0.254/32 rto_min 800j dev wlan0 + > ip route show dev wlan0 + + Notes ===== - DCCP does not travel through NAT successfully at present on many boxes. This is because the checksum covers the pseudo-header as per TCP and UDP. Linux NAT support for DCCP has been added. diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c index b9c942a09c98..dc18172b1e59 100644 --- a/net/dccp/ccids/ccid2.c +++ b/net/dccp/ccids/ccid2.c @@ -325,8 +325,9 @@ static void ccid2_rtt_estimator(struct sock *sk, const long mrtt) hc->tx_srtt = m << 3; hc->tx_mdev = m << 1; - hc->tx_mdev_max = max(TCP_RTO_MIN, hc->tx_mdev); + hc->tx_mdev_max = max(hc->tx_mdev, tcp_rto_min(sk)); hc->tx_rttvar = hc->tx_mdev_max; + hc->tx_rtt_seq = dccp_sk(sk)->dccps_gss; } else { /* Update scaled SRTT as SRTT += 1/8 * (m - SRTT) */ @@ -367,7 +368,7 @@ static void ccid2_rtt_estimator(struct sock *sk, const long mrtt) hc->tx_rttvar -= (hc->tx_rttvar - hc->tx_mdev_max) >> 2; hc->tx_rtt_seq = dccp_sk(sk)->dccps_gss; - hc->tx_mdev_max = TCP_RTO_MIN; + hc->tx_mdev_max = tcp_rto_min(sk); } } -- cgit v1.2.3-70-g09d2 From 89858ad14307a398961a0f1414b04053c1475e4f Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Sun, 29 Aug 2010 19:23:14 +0000 Subject: dccp ccid-3: use per-route RTO or TCP RTO as fallback This makes RTAX_RTO_MIN also available to CCID-3, replacing the compile-time RTO lower bound with a per-route tunable value. The original Kconfig option solved the problem that a very low RTT (in the order of HZ) can trigger too frequent and unnecessary reductions of the sending rate. This tunable does not affect the initial RTO value of 2 seconds specified in RFC 5348, section 4.2 and Appendix B. But like the hardcoded Kconfig value, it allows to adapt to network conditions. The same effect as the original Kconfig option of 100ms is now achieved by > ip route replace to unicast 192.168.0.0/24 rto_min 100j dev eth0 (assuming HZ=1000). Signed-off-by: Gerrit Renker Signed-off-by: David S. Miller --- Documentation/networking/dccp.txt | 3 +++ net/dccp/ccids/Kconfig | 31 ------------------------------- net/dccp/ccids/ccid3.c | 11 +++++------ net/dccp/ccids/ccid3.h | 2 +- 4 files changed, 9 insertions(+), 38 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index cdb64922ba10..271d524a4c8d 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -174,6 +174,9 @@ Per-route rto_min support > ip route change 10.0.0.0/24 rto_min 250j dev wlan0 > ip route add 10.0.0.254/32 rto_min 800j dev wlan0 > ip route show dev wlan0 + CCID-3 also supports the rto_min setting: it is used to define the lower + bound for the expiry of the nofeedback timer. This can be useful on LANs + with very low RTTs (e.g., loopback, Gbit ethernet). Notes diff --git a/net/dccp/ccids/Kconfig b/net/dccp/ccids/Kconfig index 8408398cd44e..0581143cb800 100644 --- a/net/dccp/ccids/Kconfig +++ b/net/dccp/ccids/Kconfig @@ -47,37 +47,6 @@ config IP_DCCP_CCID3_DEBUG If in doubt, say N. -config IP_DCCP_CCID3_RTO - int "Use higher bound for nofeedback timer" - default 100 - depends on IP_DCCP_CCID3 && EXPERIMENTAL - ---help--- - Use higher lower bound for nofeedback timer expiration. - - The TFRC nofeedback timer normally expires after the maximum of 4 - RTTs and twice the current send interval (RFC 3448, 4.3). On LANs - with a small RTT this can mean a high processing load and reduced - performance, since then the nofeedback timer is triggered very - frequently. - - This option enables to set a higher lower bound for the nofeedback - value. Values in units of milliseconds can be set here. - - A value of 0 disables this feature by enforcing the value specified - in RFC 3448. The following values have been suggested as bounds for - experimental use: - * 16-20ms to match the typical multimedia inter-frame interval - * 100ms as a reasonable compromise [default] - * 1000ms corresponds to the lower TCP RTO bound (RFC 2988, 2.4) - - The default of 100ms is a compromise between a large value for - efficient DCCP implementations, and a small value to avoid disrupting - the network in times of congestion. - - The purpose of the nofeedback timer is to slow DCCP down when there - is serious network congestion: experimenting with larger values should - therefore not be performed on WANs. - config IP_DCCP_TFRC_LIB def_bool y if IP_DCCP_CCID3 diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c index 4340672a817c..278e17069322 100644 --- a/net/dccp/ccids/ccid3.c +++ b/net/dccp/ccids/ccid3.c @@ -460,13 +460,12 @@ done_computing_x: sk->sk_write_space(sk); /* - * Update timeout interval for the nofeedback timer. - * We use a configuration option to increase the lower bound. - * This can help avoid triggering the nofeedback timer too - * often ('spinning') on LANs with small RTTs. + * Update timeout interval for the nofeedback timer. In order to control + * rate halving on networks with very low RTTs (<= 1 ms), use per-route + * tunable RTAX_RTO_MIN value as the lower bound. */ - hc->tx_t_rto = max_t(u32, 4 * hc->tx_rtt, (CONFIG_IP_DCCP_CCID3_RTO * - (USEC_PER_SEC / 1000))); + hc->tx_t_rto = max_t(u32, 4 * hc->tx_rtt, + USEC_PER_SEC/HZ * tcp_rto_min(sk)); /* * Schedule no feedback timer to expire in * max(t_RTO, 2 * s/X) = max(t_RTO, 2 * t_ipi) diff --git a/net/dccp/ccids/ccid3.h b/net/dccp/ccids/ccid3.h index b1864243be07..b7e569c22f36 100644 --- a/net/dccp/ccids/ccid3.h +++ b/net/dccp/ccids/ccid3.h @@ -42,7 +42,7 @@ #include "lib/tfrc.h" #include "../ccid.h" -/* Two seconds as per RFC 3448 4.2 */ +/* Two seconds as per RFC 5348, 4.2 */ #define TFRC_INITIAL_TIMEOUT (2 * USEC_PER_SEC) /* In usecs - half the scheduling granularity as per RFC3448 4.6 */ -- cgit v1.2.3-70-g09d2 From e98419c23b1a189c932775f7833e94cb5230a16b Mon Sep 17 00:00:00 2001 From: Nitin Gupta Date: Mon, 9 Aug 2010 22:56:56 +0530 Subject: Staging: zram: Document sysfs entries Signed-off-by: Nitin Gupta Acked-by: Pekka Enberg Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-block-zram | 99 ++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-block-zram (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-block-zram b/Documentation/ABI/testing/sysfs-block-zram new file mode 100644 index 000000000000..c8b3b48ec62c --- /dev/null +++ b/Documentation/ABI/testing/sysfs-block-zram @@ -0,0 +1,99 @@ +What: /sys/block/zram/disksize +Date: August 2010 +Contact: Nitin Gupta +Description: + The disksize file is read-write and specifies the disk size + which represents the limit on the *uncompressed* worth of data + that can be stored in this disk. + +What: /sys/block/zram/initstate +Date: August 2010 +Contact: Nitin Gupta +Description: + The disksize file is read-only and shows the initialization + state of the device. + +What: /sys/block/zram/reset +Date: August 2010 +Contact: Nitin Gupta +Description: + The disksize file is write-only and allows resetting the + device. The reset operation frees all the memory assocaited + with this device. + +What: /sys/block/zram/num_reads +Date: August 2010 +Contact: Nitin Gupta +Description: + The num_reads file is read-only and specifies the number of + reads (failed or successful) done on this device. + +What: /sys/block/zram/num_writes +Date: August 2010 +Contact: Nitin Gupta +Description: + The num_writes file is read-only and specifies the number of + writes (failed or successful) done on this device. + +What: /sys/block/zram/invalid_io +Date: August 2010 +Contact: Nitin Gupta +Description: + The invalid_io file is read-only and specifies the number of + non-page-size-aligned I/O requests issued to this device. + +What: /sys/block/zram/notify_free +Date: August 2010 +Contact: Nitin Gupta +Description: + The notify_free file is read-only and specifies the number of + swap slot free notifications received by this device. These + notifications are send to a swap block device when a swap slot + is freed. This statistic is applicable only when this disk is + being used as a swap disk. + +What: /sys/block/zram/discard +Date: August 2010 +Contact: Nitin Gupta +Description: + The discard file is read-only and specifies the number of + discard requests received by this device. These requests + provide information to block device regarding blocks which are + no longer used by filesystem. + +What: /sys/block/zram/zero_pages +Date: August 2010 +Contact: Nitin Gupta +Description: + The zero_pages file is read-only and specifies number of zero + filled pages written to this disk. No memory is allocated for + such pages. + +What: /sys/block/zram/orig_data_size +Date: August 2010 +Contact: Nitin Gupta +Description: + The orig_data_size file is read-only and specifies uncompressed + size of data stored in this disk. This excludes zero-filled + pages (zero_pages) since no memory is allocated for them. + Unit: bytes + +What: /sys/block/zram/compr_data_size +Date: August 2010 +Contact: Nitin Gupta +Description: + The compr_data_size file is read-only and specifies compressed + size of data stored in this disk. So, compression ratio can be + calculated using orig_data_size and this statistic. + Unit: bytes + +What: /sys/block/zram/mem_used_total +Date: August 2010 +Contact: Nitin Gupta +Description: + The mem_used_total file is read-only and specifies the amount + of memory, including allocator fragmentation and metadata + overhead, allocated for this disk. So, allocator space + efficiency can be calculated using compr_data_size and this + statistic. + Unit: bytes \ No newline at end of file -- cgit v1.2.3-70-g09d2 From cb7cf3da0daa9830e00640da8f7d2380f4b4de42 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 29 Aug 2010 12:30:18 +0200 Subject: HID: roccat: add driver for Roccat Pyra mouse This patch add support for Pyra mobile gaming mouse from Roccat. It provides access to profiles, settings, actual settings etc. through sysfs attributes. This driver is conceptual similar to the existing Kone driver. Userland tools can soon be found at http://sourceforge.net/projects/roccat Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/testing/sysfs-driver-hid-roccat-pyra | 98 +++ drivers/hid/Kconfig | 7 + drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 2 + drivers/hid/hid-roccat-pyra.c | 964 +++++++++++++++++++++ drivers/hid/hid-roccat-pyra.h | 186 ++++ 7 files changed, 1259 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra create mode 100644 drivers/hid/hid-roccat-pyra.c create mode 100644 drivers/hid/hid-roccat-pyra.h (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra b/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra new file mode 100644 index 000000000000..ad1125b02ff4 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra @@ -0,0 +1,98 @@ +What: /sys/bus/usb/devices/-:./actual_cpi +Date: August 2010 +Contact: Stefan Achatz +Description: It is possible to switch the cpi setting of the mouse with the + press of a button. + When read, this file returns the raw number of the actual cpi + setting reported by the mouse. This number has to be further + processed to receive the real dpi value. + + VALUE DPI + 1 400 + 2 800 + 4 1600 + + This file is readonly. + +What: /sys/bus/usb/devices/-:./actual_profile +Date: August 2010 +Contact: Stefan Achatz +Description: When read, this file returns the number of the actual profile in + range 0-4. + This file is readonly. + +What: /sys/bus/usb/devices/-:./firmware_version +Date: August 2010 +Contact: Stefan Achatz +Description: When read, this file returns the raw integer version number of the + firmware reported by the mouse. Using the integer value eases + further usage in other programs. To receive the real version + number the decimal point has to be shifted 2 positions to the + left. E.g. a returned value of 138 means 1.38 + This file is readonly. + +What: /sys/bus/usb/devices/-:./profile_settings +Date: August 2010 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_settings holds informations like resolution, sensitivity + and light effects. + When written, this file lets one write the respective profile + settings back to the mouse. The data has to be 13 bytes long. + The mouse will reject invalid data. + Which profile to write is determined by the profile number + contained in the data. + This file is writeonly. + +What: /sys/bus/usb/devices/-:./profile[1-5]_settings +Date: August 2010 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_settings holds informations like resolution, sensitivity + and light effects. + When read, these files return the respective profile settings. + The returned data is 13 bytes in size. + This file is readonly. + +What: /sys/bus/usb/devices/-:./profile_buttons +Date: August 2010 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_buttons holds informations about button layout. + When written, this file lets one write the respective profile + buttons back to the mouse. The data has to be 19 bytes long. + The mouse will reject invalid data. + Which profile to write is determined by the profile number + contained in the data. + This file is writeonly. + +What: /sys/bus/usb/devices/-:./profile[1-5]_buttons +Date: August 2010 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_buttons holds informations about button layout. + When read, these files return the respective profile buttons. + The returned data is 19 bytes in size. + This file is readonly. + +What: /sys/bus/usb/devices/-:./startup_profile +Date: August 2010 +Contact: Stefan Achatz +Description: The integer value of this attribute ranges from 0-4. + When read, this attribute returns the number of the profile + that's active when the mouse is powered on. + This file is readonly. + +What: /sys/bus/usb/devices/-:./settings +Date: August 2010 +Contact: Stefan Achatz +Description: When read, this file returns the settings stored in the mouse. + The size of the data is 3 bytes and holds information on the + startup_profile. + When written, this file lets write settings back to the mouse. + The data has to be 3 bytes long. The mouse will reject invalid + data. diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 6369ba7f96f8..b07440a172b5 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -376,6 +376,13 @@ config HID_ROCCAT_KONE ---help--- Support for Roccat Kone mouse. +config HID_ROCCAT_PYRA + tristate "Roccat Pyra mouse support" + depends on USB_HID + select HID_ROCCAT + ---help--- + Support for Roccat Pyra mouse. + config HID_SAMSUNG tristate "Samsung" depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 46f037f3df80..d224fa342187 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o +obj-$(CONFIG_HID_ROCCAT_PYRA) += hid-roccat-pyra.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o obj-$(CONFIG_HID_SONY) += hid-sony.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 0c52899be964..18608b89e76b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1367,6 +1367,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 85c6d13c9ffa..5787b1a1339f 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -459,6 +459,8 @@ #define USB_VENDOR_ID_ROCCAT 0x1e7d #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced +#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 +#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 #define USB_VENDOR_ID_SAITEK 0x06a3 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c new file mode 100644 index 000000000000..6c09c15d93ed --- /dev/null +++ b/drivers/hid/hid-roccat-pyra.c @@ -0,0 +1,964 @@ +/* + * Roccat Pyra driver for Linux + * + * Copyright (c) 2010 Stefan Achatz + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +/* + * Roccat Pyra is a mobile gamer mouse which comes in wired and wireless + * variant. Wireless variant is not tested. + * Userland tools can be found at http://sourceforge.net/projects/roccat + */ + +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" +#include "hid-roccat.h" +#include "hid-roccat-pyra.h" + +static void profile_activated(struct pyra_device *pyra, + unsigned int new_profile) +{ + pyra->actual_profile = new_profile; + pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi; +} + +static int pyra_send_control(struct usb_device *usb_dev, int value, + enum pyra_control_requests request) +{ + int len; + struct pyra_control control; + + if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS || + request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) && + (value < 0 || value > 4)) + return -EINVAL; + + control.command = PYRA_COMMAND_CONTROL; + control.value = value; + control.request = request; + + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + USB_REQ_SET_CONFIGURATION, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + PYRA_USB_COMMAND_CONTROL, 0, (char *)&control, + sizeof(struct pyra_control), + USB_CTRL_SET_TIMEOUT); + + if (len != sizeof(struct pyra_control)) + return len; + + return 0; +} + +static int pyra_receive_control_status(struct usb_device *usb_dev) +{ + int len; + struct pyra_control control; + + do { + msleep(10); + + len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | + USB_DIR_IN, + PYRA_USB_COMMAND_CONTROL, 0, (char *)&control, + sizeof(struct pyra_control), + USB_CTRL_SET_TIMEOUT); + + /* requested too early, try again */ + } while (len == -EPROTO); + + if (len == sizeof(struct pyra_control) && + control.command == PYRA_COMMAND_CONTROL && + control.request == PYRA_CONTROL_REQUEST_STATUS && + control.value == 1) + return 0; + else { + dev_err(&usb_dev->dev, "receive control status: " + "unknown response 0x%x 0x%x\n", + control.request, control.value); + return -EINVAL; + } +} + +static int pyra_get_profile_settings(struct usb_device *usb_dev, + struct pyra_profile_settings *buf, int number) +{ + int retval; + + retval = pyra_send_control(usb_dev, number, + PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); + + if (retval) + return retval; + + retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)buf, + sizeof(struct pyra_profile_settings), + USB_CTRL_SET_TIMEOUT); + + if (retval != sizeof(struct pyra_profile_settings)) + return retval; + + return 0; +} + +static int pyra_get_profile_buttons(struct usb_device *usb_dev, + struct pyra_profile_buttons *buf, int number) +{ + int retval; + + retval = pyra_send_control(usb_dev, number, + PYRA_CONTROL_REQUEST_PROFILE_BUTTONS); + + if (retval) + return retval; + + retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buf, + sizeof(struct pyra_profile_buttons), + USB_CTRL_SET_TIMEOUT); + + if (retval != sizeof(struct pyra_profile_buttons)) + return retval; + + return 0; +} + +static int pyra_get_settings(struct usb_device *usb_dev, + struct pyra_settings *buf) +{ + int len; + len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + PYRA_USB_COMMAND_SETTINGS, 0, buf, + sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT); + if (len != sizeof(struct pyra_settings)) + return -EIO; + return 0; +} + +static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf) +{ + int len; + len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + PYRA_USB_COMMAND_INFO, 0, buf, + sizeof(struct pyra_info), USB_CTRL_SET_TIMEOUT); + if (len != sizeof(struct pyra_info)) + return -EIO; + return 0; +} + +static int pyra_set_profile_settings(struct usb_device *usb_dev, + struct pyra_profile_settings const *settings) +{ + int len; + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + USB_REQ_SET_CONFIGURATION, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)settings, + sizeof(struct pyra_profile_settings), + USB_CTRL_SET_TIMEOUT); + if (len != sizeof(struct pyra_profile_settings)) + return -EIO; + if (pyra_receive_control_status(usb_dev)) + return -EIO; + return 0; +} + +static int pyra_set_profile_buttons(struct usb_device *usb_dev, + struct pyra_profile_buttons const *buttons) +{ + int len; + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + USB_REQ_SET_CONFIGURATION, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buttons, + sizeof(struct pyra_profile_buttons), + USB_CTRL_SET_TIMEOUT); + if (len != sizeof(struct pyra_profile_buttons)) + return -EIO; + if (pyra_receive_control_status(usb_dev)) + return -EIO; + return 0; +} + +static int pyra_set_settings(struct usb_device *usb_dev, + struct pyra_settings const *settings) +{ + int len; + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + USB_REQ_SET_CONFIGURATION, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + PYRA_USB_COMMAND_SETTINGS, 0, (char *)settings, + sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT); + if (len != sizeof(struct pyra_settings)) + return -EIO; + if (pyra_receive_control_status(usb_dev)) + return -EIO; + return 0; +} + +static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count, int number) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + + if (off >= sizeof(struct pyra_profile_settings)) + return 0; + + if (off + count > sizeof(struct pyra_profile_settings)) + count = sizeof(struct pyra_profile_settings) - off; + + mutex_lock(&pyra->pyra_lock); + memcpy(buf, ((char const *)&pyra->profile_settings[number]) + off, + count); + mutex_unlock(&pyra->pyra_lock); + + return count; +} + +static ssize_t pyra_sysfs_read_profile1_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_settings(fp, kobj, + attr, buf, off, count, 0); +} + +static ssize_t pyra_sysfs_read_profile2_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_settings(fp, kobj, + attr, buf, off, count, 1); +} + +static ssize_t pyra_sysfs_read_profile3_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_settings(fp, kobj, + attr, buf, off, count, 2); +} + +static ssize_t pyra_sysfs_read_profile4_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_settings(fp, kobj, + attr, buf, off, count, 3); +} + +static ssize_t pyra_sysfs_read_profile5_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_settings(fp, kobj, + attr, buf, off, count, 4); +} + +static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count, int number) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + + if (off >= sizeof(struct pyra_profile_buttons)) + return 0; + + if (off + count > sizeof(struct pyra_profile_buttons)) + count = sizeof(struct pyra_profile_buttons) - off; + + mutex_lock(&pyra->pyra_lock); + memcpy(buf, ((char const *)&pyra->profile_buttons[number]) + off, + count); + mutex_unlock(&pyra->pyra_lock); + + return count; +} + +static ssize_t pyra_sysfs_read_profile1_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_buttons(fp, kobj, + attr, buf, off, count, 0); +} + +static ssize_t pyra_sysfs_read_profile2_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_buttons(fp, kobj, + attr, buf, off, count, 1); +} + +static ssize_t pyra_sysfs_read_profile3_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_buttons(fp, kobj, + attr, buf, off, count, 2); +} + +static ssize_t pyra_sysfs_read_profile4_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_buttons(fp, kobj, + attr, buf, off, count, 3); +} + +static ssize_t pyra_sysfs_read_profile5_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_buttons(fp, kobj, + attr, buf, off, count, 4); +} + +static ssize_t pyra_sysfs_write_profile_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval = 0; + int difference; + int profile_number; + struct pyra_profile_settings *profile_settings; + + if (off != 0 || count != sizeof(struct pyra_profile_settings)) + return -EINVAL; + + profile_number = ((struct pyra_profile_settings const *)buf)->number; + profile_settings = &pyra->profile_settings[profile_number]; + + mutex_lock(&pyra->pyra_lock); + difference = memcmp(buf, profile_settings, + sizeof(struct pyra_profile_settings)); + if (difference) { + retval = pyra_set_profile_settings(usb_dev, + (struct pyra_profile_settings const *)buf); + if (!retval) + memcpy(profile_settings, buf, + sizeof(struct pyra_profile_settings)); + } + mutex_unlock(&pyra->pyra_lock); + + if (retval) + return retval; + + return sizeof(struct pyra_profile_settings); +} + +static ssize_t pyra_sysfs_write_profile_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval = 0; + int difference; + int profile_number; + struct pyra_profile_buttons *profile_buttons; + + if (off != 0 || count != sizeof(struct pyra_profile_buttons)) + return -EINVAL; + + profile_number = ((struct pyra_profile_buttons const *)buf)->number; + profile_buttons = &pyra->profile_buttons[profile_number]; + + mutex_lock(&pyra->pyra_lock); + difference = memcmp(buf, profile_buttons, + sizeof(struct pyra_profile_buttons)); + if (difference) { + retval = pyra_set_profile_buttons(usb_dev, + (struct pyra_profile_buttons const *)buf); + if (!retval) + memcpy(profile_buttons, buf, + sizeof(struct pyra_profile_buttons)); + } + mutex_unlock(&pyra->pyra_lock); + + if (retval) + return retval; + + return sizeof(struct pyra_profile_buttons); +} + +static ssize_t pyra_sysfs_read_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + + if (off >= sizeof(struct pyra_settings)) + return 0; + + if (off + count > sizeof(struct pyra_settings)) + count = sizeof(struct pyra_settings) - off; + + mutex_lock(&pyra->pyra_lock); + memcpy(buf, ((char const *)&pyra->settings) + off, count); + mutex_unlock(&pyra->pyra_lock); + + return count; +} + +static ssize_t pyra_sysfs_write_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval = 0; + int difference; + + if (off != 0 || count != sizeof(struct pyra_settings)) + return -EINVAL; + + mutex_lock(&pyra->pyra_lock); + difference = memcmp(buf, &pyra->settings, sizeof(struct pyra_settings)); + if (difference) { + retval = pyra_set_settings(usb_dev, + (struct pyra_settings const *)buf); + if (!retval) + memcpy(&pyra->settings, buf, + sizeof(struct pyra_settings)); + } + mutex_unlock(&pyra->pyra_lock); + + if (retval) + return retval; + + profile_activated(pyra, pyra->settings.startup_profile); + + return sizeof(struct pyra_settings); +} + + +static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi); +} + +static ssize_t pyra_sysfs_show_actual_profile(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_profile); +} + +static ssize_t pyra_sysfs_show_firmware_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + return snprintf(buf, PAGE_SIZE, "%d\n", pyra->firmware_version); +} + +static ssize_t pyra_sysfs_show_startup_profile(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + return snprintf(buf, PAGE_SIZE, "%d\n", pyra->settings.startup_profile); +} + +static DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL); + +static DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL); + +static DEVICE_ATTR(firmware_version, 0440, + pyra_sysfs_show_firmware_version, NULL); + +static DEVICE_ATTR(startup_profile, 0440, + pyra_sysfs_show_startup_profile, NULL); + +static struct attribute *pyra_attributes[] = { + &dev_attr_actual_cpi.attr, + &dev_attr_actual_profile.attr, + &dev_attr_firmware_version.attr, + &dev_attr_startup_profile.attr, + NULL +}; + +static struct attribute_group pyra_attribute_group = { + .attrs = pyra_attributes +}; + +static struct bin_attribute pyra_profile_settings_attr = { + .attr = { .name = "profile_settings", .mode = 0220 }, + .size = sizeof(struct pyra_profile_settings), + .write = pyra_sysfs_write_profile_settings +}; + +static struct bin_attribute pyra_profile1_settings_attr = { + .attr = { .name = "profile1_settings", .mode = 0440 }, + .size = sizeof(struct pyra_profile_settings), + .read = pyra_sysfs_read_profile1_settings +}; + +static struct bin_attribute pyra_profile2_settings_attr = { + .attr = { .name = "profile2_settings", .mode = 0440 }, + .size = sizeof(struct pyra_profile_settings), + .read = pyra_sysfs_read_profile2_settings +}; + +static struct bin_attribute pyra_profile3_settings_attr = { + .attr = { .name = "profile3_settings", .mode = 0440 }, + .size = sizeof(struct pyra_profile_settings), + .read = pyra_sysfs_read_profile3_settings +}; + +static struct bin_attribute pyra_profile4_settings_attr = { + .attr = { .name = "profile4_settings", .mode = 0440 }, + .size = sizeof(struct pyra_profile_settings), + .read = pyra_sysfs_read_profile4_settings +}; + +static struct bin_attribute pyra_profile5_settings_attr = { + .attr = { .name = "profile5_settings", .mode = 0440 }, + .size = sizeof(struct pyra_profile_settings), + .read = pyra_sysfs_read_profile5_settings +}; + +static struct bin_attribute pyra_profile_buttons_attr = { + .attr = { .name = "profile_buttons", .mode = 0220 }, + .size = sizeof(struct pyra_profile_buttons), + .write = pyra_sysfs_write_profile_buttons +}; + +static struct bin_attribute pyra_profile1_buttons_attr = { + .attr = { .name = "profile1_buttons", .mode = 0440 }, + .size = sizeof(struct pyra_profile_buttons), + .read = pyra_sysfs_read_profile1_buttons +}; + +static struct bin_attribute pyra_profile2_buttons_attr = { + .attr = { .name = "profile2_buttons", .mode = 0440 }, + .size = sizeof(struct pyra_profile_buttons), + .read = pyra_sysfs_read_profile2_buttons +}; + +static struct bin_attribute pyra_profile3_buttons_attr = { + .attr = { .name = "profile3_buttons", .mode = 0440 }, + .size = sizeof(struct pyra_profile_buttons), + .read = pyra_sysfs_read_profile3_buttons +}; + +static struct bin_attribute pyra_profile4_buttons_attr = { + .attr = { .name = "profile4_buttons", .mode = 0440 }, + .size = sizeof(struct pyra_profile_buttons), + .read = pyra_sysfs_read_profile4_buttons +}; + +static struct bin_attribute pyra_profile5_buttons_attr = { + .attr = { .name = "profile5_buttons", .mode = 0440 }, + .size = sizeof(struct pyra_profile_buttons), + .read = pyra_sysfs_read_profile5_buttons +}; + +static struct bin_attribute pyra_settings_attr = { + .attr = { .name = "settings", .mode = 0660 }, + .size = sizeof(struct pyra_settings), + .read = pyra_sysfs_read_settings, + .write = pyra_sysfs_write_settings +}; + +static int pyra_create_sysfs_attributes(struct usb_interface *intf) +{ + int retval; + + retval = sysfs_create_group(&intf->dev.kobj, &pyra_attribute_group); + if (retval) + goto exit_1; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile_settings_attr); + if (retval) + goto exit_2; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile1_settings_attr); + if (retval) + goto exit_3; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile2_settings_attr); + if (retval) + goto exit_4; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile3_settings_attr); + if (retval) + goto exit_5; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile4_settings_attr); + if (retval) + goto exit_6; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile5_settings_attr); + if (retval) + goto exit_7; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile_buttons_attr); + if (retval) + goto exit_8; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile1_buttons_attr); + if (retval) + goto exit_9; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile2_buttons_attr); + if (retval) + goto exit_10; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile3_buttons_attr); + if (retval) + goto exit_11; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile4_buttons_attr); + if (retval) + goto exit_12; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile5_buttons_attr); + if (retval) + goto exit_13; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_settings_attr); + if (retval) + goto exit_14; + + return 0; + +exit_14: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_buttons_attr); +exit_13: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_buttons_attr); +exit_12: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_buttons_attr); +exit_11: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_buttons_attr); +exit_10: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_buttons_attr); +exit_9: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_buttons_attr); +exit_8: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_settings_attr); +exit_7: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_settings_attr); +exit_6: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_settings_attr); +exit_5: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_settings_attr); +exit_4: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_settings_attr); +exit_3: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_settings_attr); +exit_2: + sysfs_remove_group(&intf->dev.kobj, &pyra_attribute_group); +exit_1: + return retval; +} + +static void pyra_remove_sysfs_attributes(struct usb_interface *intf) +{ + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_settings_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_buttons_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_buttons_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_buttons_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_buttons_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_buttons_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_buttons_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_settings_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_settings_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_settings_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_settings_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_settings_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_settings_attr); + sysfs_remove_group(&intf->dev.kobj, &pyra_attribute_group); +} + +static int pyra_init_pyra_device_struct(struct usb_device *usb_dev, + struct pyra_device *pyra) +{ + struct pyra_info *info; + int retval, i; + + mutex_init(&pyra->pyra_lock); + + info = kmalloc(sizeof(struct pyra_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + retval = pyra_get_info(usb_dev, info); + if (retval) { + kfree(info); + return retval; + } + pyra->firmware_version = info->firmware_version; + kfree(info); + + retval = pyra_get_settings(usb_dev, &pyra->settings); + if (retval) + return retval; + + for (i = 0; i < 5; ++i) { + retval = pyra_get_profile_settings(usb_dev, + &pyra->profile_settings[i], i); + if (retval) + return retval; + + retval = pyra_get_profile_buttons(usb_dev, + &pyra->profile_buttons[i], i); + if (retval) + return retval; + } + + profile_activated(pyra, pyra->settings.startup_profile); + + return 0; +} + +static int pyra_init_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct pyra_device *pyra; + int retval; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + == USB_INTERFACE_PROTOCOL_MOUSE) { + + pyra = kzalloc(sizeof(*pyra), GFP_KERNEL); + if (!pyra) { + dev_err(&hdev->dev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, pyra); + + retval = pyra_init_pyra_device_struct(usb_dev, pyra); + if (retval) { + dev_err(&hdev->dev, + "couldn't init struct pyra_device\n"); + goto exit_free; + } + + retval = roccat_connect(hdev); + if (retval < 0) { + dev_err(&hdev->dev, "couldn't init char dev\n"); + } else { + pyra->chrdev_minor = retval; + pyra->roccat_claimed = 1; + } + + retval = pyra_create_sysfs_attributes(intf); + if (retval) { + dev_err(&hdev->dev, "cannot create sysfs files\n"); + goto exit_free; + } + } else { + hid_set_drvdata(hdev, NULL); + } + + return 0; +exit_free: + kfree(pyra); + return retval; +} + +static void pyra_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct pyra_device *pyra; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + == USB_INTERFACE_PROTOCOL_MOUSE) { + pyra_remove_sysfs_attributes(intf); + pyra = hid_get_drvdata(hdev); + if (pyra->roccat_claimed) + roccat_disconnect(pyra->chrdev_minor); + kfree(hid_get_drvdata(hdev)); + } +} + +static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int retval; + + retval = hid_parse(hdev); + if (retval) { + dev_err(&hdev->dev, "parse failed\n"); + goto exit; + } + + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + dev_err(&hdev->dev, "hw start failed\n"); + goto exit; + } + + retval = pyra_init_specials(hdev); + if (retval) { + dev_err(&hdev->dev, "couldn't install mouse\n"); + goto exit_stop; + } + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void pyra_remove(struct hid_device *hdev) +{ + pyra_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static void pyra_keep_values_up_to_date(struct pyra_device *pyra, + u8 const *data) +{ + struct pyra_mouse_event_button const *button_event; + + switch (data[0]) { + case PYRA_MOUSE_REPORT_NUMBER_BUTTON: + button_event = (struct pyra_mouse_event_button const *)data; + switch (button_event->type) { + case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: + profile_activated(pyra, button_event->data1 - 1); + break; + case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: + pyra->actual_cpi = button_event->data1; + break; + } + break; + } +} + +static void pyra_report_to_chrdev(struct pyra_device const *pyra, + u8 const *data) +{ + struct pyra_roccat_report roccat_report; + struct pyra_mouse_event_button const *button_event; + + if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON) + return; + + button_event = (struct pyra_mouse_event_button const *)data; + + switch (button_event->type) { + case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: + case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: + roccat_report.type = button_event->type; + roccat_report.value = button_event->data1; + roccat_report.key = 0; + roccat_report_event(pyra->chrdev_minor, + (uint8_t const *)&roccat_report, + sizeof(struct pyra_roccat_report)); + break; + case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO: + case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT: + case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH: + if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) { + roccat_report.type = button_event->type; + roccat_report.key = button_event->data1; + roccat_report.value = pyra->actual_profile; + roccat_report_event(pyra->chrdev_minor, + (uint8_t const *)&roccat_report, + sizeof(struct pyra_roccat_report)); + } + break; + } +} + +static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct pyra_device *pyra = hid_get_drvdata(hdev); + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) + return 0; + + pyra_keep_values_up_to_date(pyra, data); + + if (pyra->roccat_claimed) + pyra_report_to_chrdev(pyra, data); + + return 0; +} + +static const struct hid_device_id pyra_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, + USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, + /* TODO add USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS after testing */ + { } +}; + +MODULE_DEVICE_TABLE(hid, pyra_devices); + +static struct hid_driver pyra_driver = { + .name = "pyra", + .id_table = pyra_devices, + .probe = pyra_probe, + .remove = pyra_remove, + .raw_event = pyra_raw_event +}; + +static int __init pyra_init(void) +{ + return hid_register_driver(&pyra_driver); +} + +static void __exit pyra_exit(void) +{ + hid_unregister_driver(&pyra_driver); +} + +module_init(pyra_init); +module_exit(pyra_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Pyra driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-pyra.h b/drivers/hid/hid-roccat-pyra.h new file mode 100644 index 000000000000..22f80a8f26f9 --- /dev/null +++ b/drivers/hid/hid-roccat-pyra.h @@ -0,0 +1,186 @@ +#ifndef __HID_ROCCAT_PYRA_H +#define __HID_ROCCAT_PYRA_H + +/* + * Copyright (c) 2010 Stefan Achatz + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include + +#pragma pack(push) +#pragma pack(1) + +struct pyra_b { + uint8_t command; /* PYRA_COMMAND_B */ + uint8_t size; /* always 3 */ + uint8_t unknown; /* 1 */ +}; + +struct pyra_control { + uint8_t command; /* PYRA_COMMAND_CONTROL */ + /* + * value is profile number for request_settings and request_buttons + * 1 if status ok for request_status + */ + uint8_t value; /* Range 0-4 */ + uint8_t request; +}; + +enum pyra_control_requests { + PYRA_CONTROL_REQUEST_STATUS = 0x00, + PYRA_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10, + PYRA_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20 +}; + +struct pyra_settings { + uint8_t command; /* PYRA_COMMAND_SETTINGS */ + uint8_t size; /* always 3 */ + uint8_t startup_profile; /* Range 0-4! */ +}; + +struct pyra_profile_settings { + uint8_t command; /* PYRA_COMMAND_PROFILE_SETTINGS */ + uint8_t size; /* always 0xd */ + uint8_t number; /* Range 0-4 */ + uint8_t xysync; + uint8_t x_sensitivity; /* 0x1-0xa */ + uint8_t y_sensitivity; + uint8_t x_cpi; /* unused */ + uint8_t y_cpi; /* this value is for x and y */ + uint8_t lightswitch; /* 0 = off, 1 = on */ + uint8_t light_effect; + uint8_t handedness; + uint16_t checksum; /* byte sum */ +}; + +struct pyra_profile_buttons { + uint8_t command; /* PYRA_COMMAND_PROFILE_BUTTONS */ + uint8_t size; /* always 0x13 */ + uint8_t number; /* Range 0-4 */ + uint8_t buttons[14]; + uint16_t checksum; /* byte sum */ +}; + +struct pyra_info { + uint8_t command; /* PYRA_COMMAND_INFO */ + uint8_t size; /* always 6 */ + uint8_t firmware_version; + uint8_t unknown1; /* always 0 */ + uint8_t unknown2; /* always 1 */ + uint8_t unknown3; /* always 0 */ +}; + +enum pyra_commands { + PYRA_COMMAND_CONTROL = 0x4, + PYRA_COMMAND_SETTINGS = 0x5, + PYRA_COMMAND_PROFILE_SETTINGS = 0x6, + PYRA_COMMAND_PROFILE_BUTTONS = 0x7, + PYRA_COMMAND_INFO = 0x9, + PYRA_COMMAND_B = 0xb +}; + +enum pyra_usb_commands { + PYRA_USB_COMMAND_CONTROL = 0x304, + PYRA_USB_COMMAND_SETTINGS = 0x305, + PYRA_USB_COMMAND_PROFILE_SETTINGS = 0x306, + PYRA_USB_COMMAND_PROFILE_BUTTONS = 0x307, + PYRA_USB_COMMAND_INFO = 0x309, + PYRA_USB_COMMAND_B = 0x30b /* writes 3 bytes */ +}; + +enum pyra_mouse_report_numbers { + PYRA_MOUSE_REPORT_NUMBER_HID = 1, + PYRA_MOUSE_REPORT_NUMBER_AUDIO = 2, + PYRA_MOUSE_REPORT_NUMBER_BUTTON = 3, +}; + +struct pyra_mouse_event_button { + uint8_t report_number; /* always 3 */ + uint8_t unknown; /* always 0 */ + uint8_t type; + uint8_t data1; + uint8_t data2; +}; + +struct pyra_mouse_event_audio { + uint8_t report_number; /* always 2 */ + uint8_t type; + uint8_t unused; /* always 0 */ +}; + +/* hid audio controls */ +enum pyra_mouse_event_audio_types { + PYRA_MOUSE_EVENT_AUDIO_TYPE_MUTE = 0xe2, + PYRA_MOUSE_EVENT_AUDIO_TYPE_VOLUME_UP = 0xe9, + PYRA_MOUSE_EVENT_AUDIO_TYPE_VOLUME_DOWN = 0xea, +}; + +enum pyra_mouse_event_button_types { + /* + * Mouse sends tilt events on report_number 1 and 3 + * Tilt events are sent repeatedly with 0.94s between first and second + * event and 0.22s on subsequent + */ + PYRA_MOUSE_EVENT_BUTTON_TYPE_TILT = 0x10, + + /* + * These are sent sequentially + * data1 contains new profile number in range 1-5 + */ + PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_1 = 0x20, + PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2 = 0x30, + + /* + * data1 = button_number (rmp index) + * data2 = pressed/released + */ + PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO = 0x40, + PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT = 0x50, + + /* + * data1 = button_number (rmp index) + */ + PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH = 0x60, + + /* data1 = new cpi */ + PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI = 0xb0, + + /* data1 and data2 = new sensitivity */ + PYRA_MOUSE_EVENT_BUTTON_TYPE_SENSITIVITY = 0xc0, + + PYRA_MOUSE_EVENT_BUTTON_TYPE_MULTIMEDIA = 0xf0, +}; + +enum { + PYRA_MOUSE_EVENT_BUTTON_PRESS = 0, + PYRA_MOUSE_EVENT_BUTTON_RELEASE = 1, +}; + +struct pyra_roccat_report { + uint8_t type; + uint8_t value; + uint8_t key; +}; + +#pragma pack(pop) + +struct pyra_device { + int actual_profile; + int actual_cpi; + int firmware_version; + int roccat_claimed; + int chrdev_minor; + struct mutex pyra_lock; + struct pyra_settings settings; + struct pyra_profile_settings profile_settings[5]; + struct pyra_profile_buttons profile_buttons[5]; +}; + +#endif -- cgit v1.2.3-70-g09d2 From ae8abfa00efb8ec550f772cbd1e1854977d06212 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Fri, 3 Sep 2010 05:47:30 +0000 Subject: ipv6: Update ip-sysctl.txt documentation for recent changes to accept_ra and forwarding Documentation for recent changes to the tunables accept_ra and forwarding. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index f350c69b2bb4..c7165f4cb792 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1014,6 +1014,12 @@ conf/interface/*: accept_ra - BOOLEAN Accept Router Advertisements; autoconfigure using them. + Possible values are: + 0 Do not accept Router Advertisements. + 1 Accept Router Advertisements if forwarding is disabled. + 2 Overrule forwarding behaviour. Accept Router Advertisements + even if forwarding is enabled. + Functional default: enabled if local forwarding is disabled. disabled if local forwarding is enabled. @@ -1075,7 +1081,12 @@ forwarding - BOOLEAN Note: It is recommended to have the same setting on all interfaces; mixed router/host scenarios are rather uncommon. - FALSE: + Possible values are: + 0 Forwarding disabled + 1 Forwarding enabled + 2 Forwarding enabled (Hybrid Mode) + + FALSE (0): By default, Host behaviour is assumed. This means: @@ -1085,18 +1096,24 @@ forwarding - BOOLEAN Advertisements (and do autoconfiguration). 4. If accept_redirects is TRUE (default), accept Redirects. - TRUE: + TRUE (1): If local forwarding is enabled, Router behaviour is assumed. This means exactly the reverse from the above: 1. IsRouter flag is set in Neighbour Advertisements. 2. Router Solicitations are not sent. - 3. Router Advertisements are ignored. + 3. Router Advertisements are ignored unless accept_ra is 2. 4. Redirects are ignored. - Default: FALSE if global forwarding is disabled (default), - otherwise TRUE. + TRUE (2): + + Hybrid mode. Same behaviour as TRUE, except for: + + 2. Router Solicitations are being sent when necessary. + + Default: 0 (disabled) if global forwarding is disabled (default), + otherwise 1 (enabled). hop_limit - INTEGER Default Hop Limit to set. -- cgit v1.2.3-70-g09d2 From b40d8ed4e42c79f8ed1cf345eed0888f4a2f0678 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 31 Aug 2010 10:28:17 +0200 Subject: topology/sysfs: Provide book id and siblings attributes Create attributes: /sys/devices/system/cpu/cpuX/topology/book_id /sys/devices/system/cpu/cpuX/topology/book_siblings which show the book id and the book siblings of a cpu. Unlike the attributes for SMT and MC these attributes are only present if CONFIG_SCHED_BOOK is set. There is no reason to pollute sysfs for every architecture with unused attributes. Signed-off-by: Heiko Carstens Signed-off-by: Peter Zijlstra LKML-Reference: <20100831082844.435648457@de.ibm.com> Signed-off-by: Ingo Molnar --- Documentation/cputopology.txt | 23 ++++++++++++++++++++--- drivers/base/topology.c | 16 +++++++++++++++- 2 files changed, 35 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cputopology.txt b/Documentation/cputopology.txt index f1c5c4bccd3e..902d3151f527 100644 --- a/Documentation/cputopology.txt +++ b/Documentation/cputopology.txt @@ -14,25 +14,39 @@ to /proc/cpuinfo. identifier (rather than the kernel's). The actual value is architecture and platform dependent. -3) /sys/devices/system/cpu/cpuX/topology/thread_siblings: +3) /sys/devices/system/cpu/cpuX/topology/book_id: + + the book ID of cpuX. Typically it is the hardware platform's + identifier (rather than the kernel's). The actual value is + architecture and platform dependent. + +4) /sys/devices/system/cpu/cpuX/topology/thread_siblings: internel kernel map of cpuX's hardware threads within the same core as cpuX -4) /sys/devices/system/cpu/cpuX/topology/core_siblings: +5) /sys/devices/system/cpu/cpuX/topology/core_siblings: internal kernel map of cpuX's hardware threads within the same physical_package_id. +6) /sys/devices/system/cpu/cpuX/topology/book_siblings: + + internal kernel map of cpuX's hardware threads within the same + book_id. + To implement it in an architecture-neutral way, a new source file, -drivers/base/topology.c, is to export the 4 attributes. +drivers/base/topology.c, is to export the 4 or 6 attributes. The two book +related sysfs files will only be created if CONFIG_SCHED_BOOK is selected. For an architecture to support this feature, it must define some of these macros in include/asm-XXX/topology.h: #define topology_physical_package_id(cpu) #define topology_core_id(cpu) +#define topology_book_id(cpu) #define topology_thread_cpumask(cpu) #define topology_core_cpumask(cpu) +#define topology_book_cpumask(cpu) The type of **_id is int. The type of siblings is (const) struct cpumask *. @@ -45,6 +59,9 @@ not defined by include/asm-XXX/topology.h: 3) thread_siblings: just the given CPU 4) core_siblings: just the given CPU +For architectures that don't support books (CONFIG_SCHED_BOOK) there are no +default definitions for topology_book_id() and topology_book_cpumask(). + Additionally, CPU topology information is provided under /sys/devices/system/cpu and includes these files. The internal source for the output is in brackets ("[]"). diff --git a/drivers/base/topology.c b/drivers/base/topology.c index 9fc630ce1ddb..f6f37a05a0c3 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -45,7 +45,8 @@ static ssize_t show_##name(struct sys_device *dev, \ return sprintf(buf, "%d\n", topology_##name(cpu)); \ } -#if defined(topology_thread_cpumask) || defined(topology_core_cpumask) +#if defined(topology_thread_cpumask) || defined(topology_core_cpumask) || \ + defined(topology_book_cpumask) static ssize_t show_cpumap(int type, const struct cpumask *mask, char *buf) { ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf; @@ -114,6 +115,14 @@ define_siblings_show_func(core_cpumask); define_one_ro_named(core_siblings, show_core_cpumask); define_one_ro_named(core_siblings_list, show_core_cpumask_list); +#ifdef CONFIG_SCHED_BOOK +define_id_show_func(book_id); +define_one_ro(book_id); +define_siblings_show_func(book_cpumask); +define_one_ro_named(book_siblings, show_book_cpumask); +define_one_ro_named(book_siblings_list, show_book_cpumask_list); +#endif + static struct attribute *default_attrs[] = { &attr_physical_package_id.attr, &attr_core_id.attr, @@ -121,6 +130,11 @@ static struct attribute *default_attrs[] = { &attr_thread_siblings_list.attr, &attr_core_siblings.attr, &attr_core_siblings_list.attr, +#ifdef CONFIG_SCHED_BOOK + &attr_book_id.attr, + &attr_book_siblings.attr, + &attr_book_siblings_list.attr, +#endif NULL }; -- cgit v1.2.3-70-g09d2 From 04ccc65cd1f57aee861708e08cd2272c5a0d088c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 3 Sep 2010 11:56:17 +0200 Subject: block: update documentation for REQ_FLUSH / REQ_FUA Signed-off-by: Christoph Hellwig Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- Documentation/block/00-INDEX | 4 +- Documentation/block/barrier.txt | 261 ------------------------ Documentation/block/writeback_cache_control.txt | 86 ++++++++ 3 files changed, 88 insertions(+), 263 deletions(-) delete mode 100644 Documentation/block/barrier.txt create mode 100644 Documentation/block/writeback_cache_control.txt (limited to 'Documentation') diff --git a/Documentation/block/00-INDEX b/Documentation/block/00-INDEX index a406286f6f3e..d111e3b23db0 100644 --- a/Documentation/block/00-INDEX +++ b/Documentation/block/00-INDEX @@ -1,7 +1,5 @@ 00-INDEX - This file -barrier.txt - - I/O Barriers biodoc.txt - Notes on the Generic Block Layer Rewrite in Linux 2.5 capability.txt @@ -16,3 +14,5 @@ stat.txt - Block layer statistics in /sys/block//stat switching-sched.txt - Switching I/O schedulers at runtime +writeback_cache_control.txt + - Control of volatile write back caches diff --git a/Documentation/block/barrier.txt b/Documentation/block/barrier.txt deleted file mode 100644 index 2c2f24f634e4..000000000000 --- a/Documentation/block/barrier.txt +++ /dev/null @@ -1,261 +0,0 @@ -I/O Barriers -============ -Tejun Heo , July 22 2005 - -I/O barrier requests are used to guarantee ordering around the barrier -requests. Unless you're crazy enough to use disk drives for -implementing synchronization constructs (wow, sounds interesting...), -the ordering is meaningful only for write requests for things like -journal checkpoints. All requests queued before a barrier request -must be finished (made it to the physical medium) before the barrier -request is started, and all requests queued after the barrier request -must be started only after the barrier request is finished (again, -made it to the physical medium). - -In other words, I/O barrier requests have the following two properties. - -1. Request ordering - -Requests cannot pass the barrier request. Preceding requests are -processed before the barrier and following requests after. - -Depending on what features a drive supports, this can be done in one -of the following three ways. - -i. For devices which have queue depth greater than 1 (TCQ devices) and -support ordered tags, block layer can just issue the barrier as an -ordered request and the lower level driver, controller and drive -itself are responsible for making sure that the ordering constraint is -met. Most modern SCSI controllers/drives should support this. - -NOTE: SCSI ordered tag isn't currently used due to limitation in the - SCSI midlayer, see the following random notes section. - -ii. For devices which have queue depth greater than 1 but don't -support ordered tags, block layer ensures that the requests preceding -a barrier request finishes before issuing the barrier request. Also, -it defers requests following the barrier until the barrier request is -finished. Older SCSI controllers/drives and SATA drives fall in this -category. - -iii. Devices which have queue depth of 1. This is a degenerate case -of ii. Just keeping issue order suffices. Ancient SCSI -controllers/drives and IDE drives are in this category. - -2. Forced flushing to physical medium - -Again, if you're not gonna do synchronization with disk drives (dang, -it sounds even more appealing now!), the reason you use I/O barriers -is mainly to protect filesystem integrity when power failure or some -other events abruptly stop the drive from operating and possibly make -the drive lose data in its cache. So, I/O barriers need to guarantee -that requests actually get written to non-volatile medium in order. - -There are four cases, - -i. No write-back cache. Keeping requests ordered is enough. - -ii. Write-back cache but no flush operation. There's no way to -guarantee physical-medium commit order. This kind of devices can't to -I/O barriers. - -iii. Write-back cache and flush operation but no FUA (forced unit -access). We need two cache flushes - before and after the barrier -request. - -iv. Write-back cache, flush operation and FUA. We still need one -flush to make sure requests preceding a barrier are written to medium, -but post-barrier flush can be avoided by using FUA write on the -barrier itself. - - -How to support barrier requests in drivers ------------------------------------------- - -All barrier handling is done inside block layer proper. All low level -drivers have to are implementing its prepare_flush_fn and using one -the following two functions to indicate what barrier type it supports -and how to prepare flush requests. Note that the term 'ordered' is -used to indicate the whole sequence of performing barrier requests -including draining and flushing. - -typedef void (prepare_flush_fn)(struct request_queue *q, struct request *rq); - -int blk_queue_ordered(struct request_queue *q, unsigned ordered, - prepare_flush_fn *prepare_flush_fn); - -@q : the queue in question -@ordered : the ordered mode the driver/device supports -@prepare_flush_fn : this function should prepare @rq such that it - flushes cache to physical medium when executed - -For example, SCSI disk driver's prepare_flush_fn looks like the -following. - -static void sd_prepare_flush(struct request_queue *q, struct request *rq) -{ - memset(rq->cmd, 0, sizeof(rq->cmd)); - rq->cmd_type = REQ_TYPE_BLOCK_PC; - rq->timeout = SD_TIMEOUT; - rq->cmd[0] = SYNCHRONIZE_CACHE; - rq->cmd_len = 10; -} - -The following seven ordered modes are supported. The following table -shows which mode should be used depending on what features a -device/driver supports. In the leftmost column of table, -QUEUE_ORDERED_ prefix is omitted from the mode names to save space. - -The table is followed by description of each mode. Note that in the -descriptions of QUEUE_ORDERED_DRAIN*, '=>' is used whereas '->' is -used for QUEUE_ORDERED_TAG* descriptions. '=>' indicates that the -preceding step must be complete before proceeding to the next step. -'->' indicates that the next step can start as soon as the previous -step is issued. - - write-back cache ordered tag flush FUA ------------------------------------------------------------------------ -NONE yes/no N/A no N/A -DRAIN no no N/A N/A -DRAIN_FLUSH yes no yes no -DRAIN_FUA yes no yes yes -TAG no yes N/A N/A -TAG_FLUSH yes yes yes no -TAG_FUA yes yes yes yes - - -QUEUE_ORDERED_NONE - I/O barriers are not needed and/or supported. - - Sequence: N/A - -QUEUE_ORDERED_DRAIN - Requests are ordered by draining the request queue and cache - flushing isn't needed. - - Sequence: drain => barrier - -QUEUE_ORDERED_DRAIN_FLUSH - Requests are ordered by draining the request queue and both - pre-barrier and post-barrier cache flushings are needed. - - Sequence: drain => preflush => barrier => postflush - -QUEUE_ORDERED_DRAIN_FUA - Requests are ordered by draining the request queue and - pre-barrier cache flushing is needed. By using FUA on barrier - request, post-barrier flushing can be skipped. - - Sequence: drain => preflush => barrier - -QUEUE_ORDERED_TAG - Requests are ordered by ordered tag and cache flushing isn't - needed. - - Sequence: barrier - -QUEUE_ORDERED_TAG_FLUSH - Requests are ordered by ordered tag and both pre-barrier and - post-barrier cache flushings are needed. - - Sequence: preflush -> barrier -> postflush - -QUEUE_ORDERED_TAG_FUA - Requests are ordered by ordered tag and pre-barrier cache - flushing is needed. By using FUA on barrier request, - post-barrier flushing can be skipped. - - Sequence: preflush -> barrier - - -Random notes/caveats --------------------- - -* SCSI layer currently can't use TAG ordering even if the drive, -controller and driver support it. The problem is that SCSI midlayer -request dispatch function is not atomic. It releases queue lock and -switch to SCSI host lock during issue and it's possible and likely to -happen in time that requests change their relative positions. Once -this problem is solved, TAG ordering can be enabled. - -* Currently, no matter which ordered mode is used, there can be only -one barrier request in progress. All I/O barriers are held off by -block layer until the previous I/O barrier is complete. This doesn't -make any difference for DRAIN ordered devices, but, for TAG ordered -devices with very high command latency, passing multiple I/O barriers -to low level *might* be helpful if they are very frequent. Well, this -certainly is a non-issue. I'm writing this just to make clear that no -two I/O barrier is ever passed to low-level driver. - -* Completion order. Requests in ordered sequence are issued in order -but not required to finish in order. Barrier implementation can -handle out-of-order completion of ordered sequence. IOW, the requests -MUST be processed in order but the hardware/software completion paths -are allowed to reorder completion notifications - eg. current SCSI -midlayer doesn't preserve completion order during error handling. - -* Requeueing order. Low-level drivers are free to requeue any request -after they removed it from the request queue with -blkdev_dequeue_request(). As barrier sequence should be kept in order -when requeued, generic elevator code takes care of putting requests in -order around barrier. See blk_ordered_req_seq() and -ELEVATOR_INSERT_REQUEUE handling in __elv_add_request() for details. - -Note that block drivers must not requeue preceding requests while -completing latter requests in an ordered sequence. Currently, no -error checking is done against this. - -* Error handling. Currently, block layer will report error to upper -layer if any of requests in an ordered sequence fails. Unfortunately, -this doesn't seem to be enough. Look at the following request flow. -QUEUE_ORDERED_TAG_FLUSH is in use. - - [0] [1] [2] [3] [pre] [barrier] [post] < [4] [5] [6] ... > - still in elevator - -Let's say request [2], [3] are write requests to update file system -metadata (journal or whatever) and [barrier] is used to mark that -those updates are valid. Consider the following sequence. - - i. Requests [0] ~ [post] leaves the request queue and enters - low-level driver. - ii. After a while, unfortunately, something goes wrong and the - drive fails [2]. Note that any of [0], [1] and [3] could have - completed by this time, but [pre] couldn't have been finished - as the drive must process it in order and it failed before - processing that command. - iii. Error handling kicks in and determines that the error is - unrecoverable and fails [2], and resumes operation. - iv. [pre] [barrier] [post] gets processed. - v. *BOOM* power fails - -The problem here is that the barrier request is *supposed* to indicate -that filesystem update requests [2] and [3] made it safely to the -physical medium and, if the machine crashes after the barrier is -written, filesystem recovery code can depend on that. Sadly, that -isn't true in this case anymore. IOW, the success of a I/O barrier -should also be dependent on success of some of the preceding requests, -where only upper layer (filesystem) knows what 'some' is. - -This can be solved by implementing a way to tell the block layer which -requests affect the success of the following barrier request and -making lower lever drivers to resume operation on error only after -block layer tells it to do so. - -As the probability of this happening is very low and the drive should -be faulty, implementing the fix is probably an overkill. But, still, -it's there. - -* In previous drafts of barrier implementation, there was fallback -mechanism such that, if FUA or ordered TAG fails, less fancy ordered -mode can be selected and the failed barrier request is retried -automatically. The rationale for this feature was that as FUA is -pretty new in ATA world and ordered tag was never used widely, there -could be devices which report to support those features but choke when -actually given such requests. - - This was removed for two reasons 1. it's an overkill 2. it's -impossible to implement properly when TAG ordering is used as low -level drivers resume after an error automatically. If it's ever -needed adding it back and modifying low level drivers accordingly -shouldn't be difficult. diff --git a/Documentation/block/writeback_cache_control.txt b/Documentation/block/writeback_cache_control.txt new file mode 100644 index 000000000000..83407d36630a --- /dev/null +++ b/Documentation/block/writeback_cache_control.txt @@ -0,0 +1,86 @@ + +Explicit volatile write back cache control +===================================== + +Introduction +------------ + +Many storage devices, especially in the consumer market, come with volatile +write back caches. That means the devices signal I/O completion to the +operating system before data actually has hit the non-volatile storage. This +behavior obviously speeds up various workloads, but it means the operating +system needs to force data out to the non-volatile storage when it performs +a data integrity operation like fsync, sync or an unmount. + +The Linux block layer provides two simple mechanisms that let filesystems +control the caching behavior of the storage device. These mechanisms are +a forced cache flush, and the Force Unit Access (FUA) flag for requests. + + +Explicit cache flushes +---------------------- + +The REQ_FLUSH flag can be OR ed into the r/w flags of a bio submitted from +the filesystem and will make sure the volatile cache of the storage device +has been flushed before the actual I/O operation is started. This explicitly +guarantees that previously completed write requests are on non-volatile +storage before the flagged bio starts. In addition the REQ_FLUSH flag can be +set on an otherwise empty bio structure, which causes only an explicit cache +flush without any dependent I/O. It is recommend to use +the blkdev_issue_flush() helper for a pure cache flush. + + +Forced Unit Access +----------------- + +The REQ_FUA flag can be OR ed into the r/w flags of a bio submitted from the +filesystem and will make sure that I/O completion for this request is only +signaled after the data has been committed to non-volatile storage. + + +Implementation details for filesystems +-------------------------------------- + +Filesystems can simply set the REQ_FLUSH and REQ_FUA bits and do not have to +worry if the underlying devices need any explicit cache flushing and how +the Forced Unit Access is implemented. The REQ_FLUSH and REQ_FUA flags +may both be set on a single bio. + + +Implementation details for make_request_fn based block drivers +-------------------------------------------------------------- + +These drivers will always see the REQ_FLUSH and REQ_FUA bits as they sit +directly below the submit_bio interface. For remapping drivers the REQ_FUA +bits need to be propagated to underlying devices, and a global flush needs +to be implemented for bios with the REQ_FLUSH bit set. For real device +drivers that do not have a volatile cache the REQ_FLUSH and REQ_FUA bits +on non-empty bios can simply be ignored, and REQ_FLUSH requests without +data can be completed successfully without doing any work. Drivers for +devices with volatile caches need to implement the support for these +flags themselves without any help from the block layer. + + +Implementation details for request_fn based block drivers +-------------------------------------------------------------- + +For devices that do not support volatile write caches there is no driver +support required, the block layer completes empty REQ_FLUSH requests before +entering the driver and strips off the REQ_FLUSH and REQ_FUA bits from +requests that have a payload. For devices with volatile write caches the +driver needs to tell the block layer that it supports flushing caches by +doing: + + blk_queue_flush(sdkp->disk->queue, REQ_FLUSH); + +and handle empty REQ_FLUSH requests in its prep_fn/request_fn. Note that +REQ_FLUSH requests with a payload are automatically turned into a sequence +of an empty REQ_FLUSH request followed by the actual write by the block +layer. For devices that also support the FUA bit the block layer needs +to be told to pass through the REQ_FUA bit using: + + blk_queue_flush(sdkp->disk->queue, REQ_FLUSH | REQ_FUA); + +and the driver must handle write requests that have the REQ_FUA bit set +in prep_fn/request_fn. If the FUA bit is not natively supported the block +layer turns it into an empty REQ_FLUSH request after the actual write. -- cgit v1.2.3-70-g09d2 From 7bc9fddab074d6bb630344e1969e28d20b140621 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 3 Sep 2010 11:56:18 +0200 Subject: lguest: replace VIRTIO_F_BARRIER support with VIRTIO_F_FLUSH support VIRTIO_F_BARRIER is deprecated. Replace it with VIRTIO_F_FLUSH support. Signed-off-by: Tejun Heo Cc: Rusty Russell Cc: Christoph Hellwig Signed-off-by: Jens Axboe --- Documentation/lguest/lguest.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) (limited to 'Documentation') diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c index e9ce3c554514..fbc64b331436 100644 --- a/Documentation/lguest/lguest.c +++ b/Documentation/lguest/lguest.c @@ -1638,15 +1638,6 @@ static void blk_request(struct virtqueue *vq) */ off = out->sector * 512; - /* - * The block device implements "barriers", where the Guest indicates - * that it wants all previous writes to occur before this write. We - * don't have a way of asking our kernel to do a barrier, so we just - * synchronize all the data in the file. Pretty poor, no? - */ - if (out->type & VIRTIO_BLK_T_BARRIER) - fdatasync(vblk->fd); - /* * In general the virtio block driver is allowed to try SCSI commands. * It'd be nice if we supported eject, for example, but we don't. @@ -1679,6 +1670,13 @@ static void blk_request(struct virtqueue *vq) /* Die, bad Guest, die. */ errx(1, "Write past end %llu+%u", off, ret); } + + wlen = sizeof(*in); + *in = (ret >= 0 ? VIRTIO_BLK_S_OK : VIRTIO_BLK_S_IOERR); + } else if (out->type & VIRTIO_BLK_T_FLUSH) { + /* Flush */ + ret = fdatasync(vblk->fd); + verbose("FLUSH fdatasync: %i\n", ret); wlen = sizeof(*in); *in = (ret >= 0 ? VIRTIO_BLK_S_OK : VIRTIO_BLK_S_IOERR); } else { @@ -1702,15 +1700,6 @@ static void blk_request(struct virtqueue *vq) } } - /* - * OK, so we noted that it was pretty poor to use an fdatasync as a - * barrier. But Christoph Hellwig points out that we need a sync - * *afterwards* as well: "Barriers specify no reordering to the front - * or the back." And Jens Axboe confirmed it, so here we are: - */ - if (out->type & VIRTIO_BLK_T_BARRIER) - fdatasync(vblk->fd); - /* Finished that request. */ add_used(vq, head, wlen); } @@ -1735,8 +1724,8 @@ static void setup_block_file(const char *filename) vblk->fd = open_or_die(filename, O_RDWR|O_LARGEFILE); vblk->len = lseek64(vblk->fd, 0, SEEK_END); - /* We support barriers. */ - add_feature(dev, VIRTIO_BLK_F_BARRIER); + /* We support FLUSH. */ + add_feature(dev, VIRTIO_BLK_F_FLUSH); /* Tell Guest how many sectors this device has. */ conf.capacity = cpu_to_le64(vblk->len / 512); -- cgit v1.2.3-70-g09d2 From 274a517ec13b3b47047b97a2e7c912138c9806e0 Mon Sep 17 00:00:00 2001 From: Rémi Denis-Courmont Date: Wed, 15 Sep 2010 12:30:15 +0000 Subject: Phonet: resource routing documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- Documentation/networking/phonet.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'Documentation') diff --git a/Documentation/networking/phonet.txt b/Documentation/networking/phonet.txt index 6e8ce09f9c73..cf76608a2d35 100644 --- a/Documentation/networking/phonet.txt +++ b/Documentation/networking/phonet.txt @@ -112,6 +112,22 @@ However, connect() and getpeername() are not supported, as they did not seem useful with Phonet usages (could be added easily). +Resource subscription +--------------------- + +A Phonet datagram socket can be subscribed to any number of 8-bits +Phonet resources, as follow: + + uint32_t res = 0xXX; + ioctl(fd, SIOCPNADDRESOURCE, &res); + +Subscription is similarly cancelled using the SIOCPNDELRESOURCE I/O +control request, or when the socket is closed. + +Note that no more than one socket can be subcribed to any given +resource at a time. If not, ioctl() will return EBUSY. + + Phonet Pipe protocol -------------------- -- cgit v1.2.3-70-g09d2 From 2786c4e5e54802c34297e55050fef3e862a27b3f Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 15 Sep 2010 17:06:38 -0400 Subject: blkio: Documentation Update o Documentation update Signed-off-by: Vivek Goyal Signed-off-by: Jens Axboe --- Documentation/cgroups/blkio-controller.txt | 106 ++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cgroups/blkio-controller.txt b/Documentation/cgroups/blkio-controller.txt index 48e0b21b0059..2f5613fbe680 100644 --- a/Documentation/cgroups/blkio-controller.txt +++ b/Documentation/cgroups/blkio-controller.txt @@ -8,12 +8,17 @@ both at leaf nodes as well as at intermediate nodes in a storage hierarchy. Plan is to use the same cgroup based management interface for blkio controller and based on user options switch IO policies in the background. -In the first phase, this patchset implements proportional weight time based -division of disk policy. It is implemented in CFQ. Hence this policy takes -effect only on leaf nodes when CFQ is being used. +Currently two IO control policies are implemented. First one is proportional +weight time based division of disk policy. It is implemented in CFQ. Hence +this policy takes effect only on leaf nodes when CFQ is being used. The second +one is throttling policy which can be used to specify upper IO rate limits +on devices. This policy is implemented in generic block layer and can be +used on leaf nodes as well as higher level logical devices like device mapper. HOWTO ===== +Proportional Weight division of bandwidth +----------------------------------------- You can do a very simple testing of running two dd threads in two different cgroups. Here is what you can do. @@ -55,6 +60,35 @@ cgroups. Here is what you can do. group dispatched to the disk. We provide fairness in terms of disk time, so ideally io.disk_time of cgroups should be in proportion to the weight. +Throttling/Upper Limit policy +----------------------------- +- Enable Block IO controller + CONFIG_BLK_CGROUP=y + +- Enable throttling in block layer + CONFIG_BLK_DEV_THROTTLING=y + +- Mount blkio controller + mount -t cgroup -o blkio none /cgroup/blkio + +- Specify a bandwidth rate on particular device for root group. The format + for policy is ": ". + + echo "8:16 1048576" > /cgroup/blkio/blkio.read_bps_device + + Above will put a limit of 1MB/second on reads happening for root group + on device having major/minor number 8:16. + +- Run dd to read a file and see if rate is throttled to 1MB/s or not. + + # dd if=/mnt/common/zerofile of=/dev/null bs=4K count=1024 + # iflag=direct + 1024+0 records in + 1024+0 records out + 4194304 bytes (4.2 MB) copied, 4.0001 s, 1.0 MB/s + + Limits for writes can be put using blkio.write_bps_device file. + Various user visible config options =================================== CONFIG_BLK_CGROUP @@ -68,8 +102,13 @@ CONFIG_CFQ_GROUP_IOSCHED - Enables group scheduling in CFQ. Currently only 1 level of group creation is allowed. +CONFIG_BLK_DEV_THROTTLING + - Enable block device throttling support in block layer. + Details of cgroup files ======================= +Proportional weight policy files +-------------------------------- - blkio.weight - Specifies per cgroup weight. This is default weight of the group on all the devices until and unless overridden by per device rule. @@ -210,6 +249,67 @@ Details of cgroup files and minor number of the device and third field specifies the number of times a group was dequeued from a particular device. +Throttling/Upper limit policy files +----------------------------------- +- blkio.throttle.read_bps_device + - Specifies upper limit on READ rate from the device. IO rate is + specified in bytes per second. Rules are per deivce. Following is + the format. + + echo ": " > /cgrp/blkio.read_bps_device + +- blkio.throttle.write_bps_device + - Specifies upper limit on WRITE rate to the device. IO rate is + specified in bytes per second. Rules are per deivce. Following is + the format. + + echo ": " > /cgrp/blkio.write_bps_device + +- blkio.throttle.read_iops_device + - Specifies upper limit on READ rate from the device. IO rate is + specified in IO per second. Rules are per deivce. Following is + the format. + + echo ": " > /cgrp/blkio.read_iops_device + +- blkio.throttle.write_iops_device + - Specifies upper limit on WRITE rate to the device. IO rate is + specified in io per second. Rules are per deivce. Following is + the format. + + echo ": " > /cgrp/blkio.write_iops_device + +Note: If both BW and IOPS rules are specified for a device, then IO is + subjectd to both the constraints. + +- blkio.throttle.io_serviced + - Number of IOs (bio) completed to/from the disk by the group (as + seen by throttling policy). These are further divided by the type + of operation - read or write, sync or async. First two fields specify + the major and minor number of the device, third field specifies the + operation type and the fourth field specifies the number of IOs. + + blkio.io_serviced does accounting as seen by CFQ and counts are in + number of requests (struct request). On the other hand, + blkio.throttle.io_serviced counts number of IO in terms of number + of bios as seen by throttling policy. These bios can later be + merged by elevator and total number of requests completed can be + lesser. + +- blkio.throttle.io_service_bytes + - Number of bytes transferred to/from the disk by the group. These + are further divided by the type of operation - read or write, sync + or async. First two fields specify the major and minor number of the + device, third field specifies the operation type and the fourth field + specifies the number of bytes. + + These numbers should roughly be same as blkio.io_service_bytes as + updated by CFQ. The difference between two is that + blkio.io_service_bytes will not be updated if CFQ is not operating + on request queue. + +Common files among various policies +----------------------------------- - blkio.reset_stats - Writing an int to this file will result in resetting all the stats for that cgroup. -- cgit v1.2.3-70-g09d2 From dc880abef75e7c62c9048171f5112500f36a9244 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 6 Jul 2010 18:54:47 +0200 Subject: drm: use noop_llseek The drm device drivers currently allow seeking on the character device but never care about the actual file position. When we change the default llseek operation to be no_llseek, calling llseek on a drm device would return an error condition, which is an API change. Explicitly setting noop_llseek lets us keep the current API. Signed-off-by: Arnd Bergmann Cc: David Airlie Cc: dri-devel@lists.freedesktop.org --- Documentation/DocBook/drm.tmpl | 1 + drivers/gpu/drm/i810/i810_drv.c | 1 + drivers/gpu/drm/i830/i830_drv.c | 1 + drivers/gpu/drm/i915/i915_drv.c | 1 + drivers/gpu/drm/mga/mga_drv.c | 1 + drivers/gpu/drm/nouveau/nouveau_drv.c | 1 + drivers/gpu/drm/r128/r128_drv.c | 1 + drivers/gpu/drm/radeon/radeon_drv.c | 1 + drivers/gpu/drm/savage/savage_drv.c | 1 + drivers/gpu/drm/sis/sis_drv.c | 1 + drivers/gpu/drm/tdfx/tdfx_drv.c | 1 + drivers/gpu/drm/via/via_drv.c | 1 + drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 1 + 13 files changed, 13 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 910c923a9b86..2861055afd7a 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -136,6 +136,7 @@ #ifdef CONFIG_COMPAT .compat_ioctl = i915_compat_ioctl, #endif + .llseek = noop_llseek, }, .pci_driver = { .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c index b4250b2cac1f..fe69914ce507 100644 --- a/drivers/gpu/drm/i810/i810_drv.c +++ b/drivers/gpu/drm/i810/i810_drv.c @@ -63,6 +63,7 @@ static struct drm_driver driver = { .mmap = drm_mmap, .poll = drm_poll, .fasync = drm_fasync, + .llseek = noop_llseek, }, .pci_driver = { diff --git a/drivers/gpu/drm/i830/i830_drv.c b/drivers/gpu/drm/i830/i830_drv.c index a5c66aa82f0c..5b6298b24e24 100644 --- a/drivers/gpu/drm/i830/i830_drv.c +++ b/drivers/gpu/drm/i830/i830_drv.c @@ -74,6 +74,7 @@ static struct drm_driver driver = { .mmap = drm_mmap, .poll = drm_poll, .fasync = drm_fasync, + .llseek = noop_llseek, }, .pci_driver = { diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 216deb579785..a6fdb481500c 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -547,6 +547,7 @@ static struct drm_driver driver = { #ifdef CONFIG_COMPAT .compat_ioctl = i915_compat_ioctl, #endif + .llseek = noop_llseek, }, .pci_driver = { diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c index 26d0d8ced80d..ac64f0b0392e 100644 --- a/drivers/gpu/drm/mga/mga_drv.c +++ b/drivers/gpu/drm/mga/mga_drv.c @@ -75,6 +75,7 @@ static struct drm_driver driver = { #ifdef CONFIG_COMPAT .compat_ioctl = mga_compat_ioctl, #endif + .llseek = noop_llseek, }, .pci_driver = { .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 1de5eb53e016..eb15345162a0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -393,6 +393,7 @@ static struct drm_driver driver = { #if defined(CONFIG_COMPAT) .compat_ioctl = nouveau_compat_ioctl, #endif + .llseek = noop_llseek, }, .pci_driver = { .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c index 1e2971f13aa1..d42c76c23714 100644 --- a/drivers/gpu/drm/r128/r128_drv.c +++ b/drivers/gpu/drm/r128/r128_drv.c @@ -71,6 +71,7 @@ static struct drm_driver driver = { #ifdef CONFIG_COMPAT .compat_ioctl = r128_compat_ioctl, #endif + .llseek = noop_llseek, }, .pci_driver = { .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 795403b0e2cd..29c1237c2e7b 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -219,6 +219,7 @@ static struct drm_driver driver_old = { #ifdef CONFIG_COMPAT .compat_ioctl = radeon_compat_ioctl, #endif + .llseek = noop_llseek, }, .pci_driver = { diff --git a/drivers/gpu/drm/savage/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c index 021de44c15ab..2a2830f5a840 100644 --- a/drivers/gpu/drm/savage/savage_drv.c +++ b/drivers/gpu/drm/savage/savage_drv.c @@ -54,6 +54,7 @@ static struct drm_driver driver = { .mmap = drm_mmap, .poll = drm_poll, .fasync = drm_fasync, + .llseek = noop_llseek, }, .pci_driver = { diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c index 776bf9e9ea1a..4bb10ef6676a 100644 --- a/drivers/gpu/drm/sis/sis_drv.c +++ b/drivers/gpu/drm/sis/sis_drv.c @@ -83,6 +83,7 @@ static struct drm_driver driver = { .mmap = drm_mmap, .poll = drm_poll, .fasync = drm_fasync, + .llseek = noop_llseek, }, .pci_driver = { .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c index ec5a43e65722..640567ef713d 100644 --- a/drivers/gpu/drm/tdfx/tdfx_drv.c +++ b/drivers/gpu/drm/tdfx/tdfx_drv.c @@ -52,6 +52,7 @@ static struct drm_driver driver = { .mmap = drm_mmap, .poll = drm_poll, .fasync = drm_fasync, + .llseek = noop_llseek, }, .pci_driver = { .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c index 7a1b210401e0..b8984a5ae521 100644 --- a/drivers/gpu/drm/via/via_drv.c +++ b/drivers/gpu/drm/via/via_drv.c @@ -62,6 +62,7 @@ static struct drm_driver driver = { .mmap = drm_mmap, .poll = drm_poll, .fasync = drm_fasync, + .llseek = noop_llseek, }, .pci_driver = { .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 72ec2e2b6e97..d237190b888a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -745,6 +745,7 @@ static struct drm_driver driver = { #if defined(CONFIG_COMPAT) .compat_ioctl = drm_compat_ioctl, #endif + .llseek = noop_llseek, }, .pci_driver = { .name = VMWGFX_DRIVER_NAME, -- cgit v1.2.3-70-g09d2 From 306a075362a288683f6346185f97dd0e06df19da Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 17 Sep 2010 10:54:37 -0400 Subject: NFS: Allow NFSROOT debugging messages to be enabled dynamically As a convenience, introduce a kernel command line option to enable NFSROOT debugging messages. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- Documentation/filesystems/nfs/nfsroot.txt | 22 ++++++++++++++++++++++ Documentation/kernel-parameters.txt | 5 ++++- fs/nfs/nfsroot.c | 19 +++++++++++++------ 3 files changed, 39 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/nfs/nfsroot.txt b/Documentation/filesystems/nfs/nfsroot.txt index f2430a7974e1..90c71c6f0d00 100644 --- a/Documentation/filesystems/nfs/nfsroot.txt +++ b/Documentation/filesystems/nfs/nfsroot.txt @@ -159,6 +159,28 @@ ip=:::::: Default: any +nfsrootdebug + + This parameter enables debugging messages to appear in the kernel + log at boot time so that administrators can verify that the correct + NFS mount options, server address, and root path are passed to the + NFS client. + + +rdinit= + + To specify which file contains the program that starts system + initialization, administrators can use this command line parameter. + The default value of this parameter is "/init". If the specified + file exists and the kernel can execute it, root filesystem related + kernel command line parameters, including `nfsroot=', are ignored. + + A description of the process of mounting the root file system can be + found in: + + Documentation/early-userspace/README + + 3.) Boot Loader diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index f084af0cb8e0..0fe70b2c4194 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1532,12 +1532,15 @@ and is between 256 and 4096 characters. It is defined in the file 1 to enable accounting Default value is 0. - nfsaddrs= [NFS] + nfsaddrs= [NFS] Deprecated. Use ip= instead. See Documentation/filesystems/nfs/nfsroot.txt. nfsroot= [NFS] nfs root filesystem for disk-less boxes. See Documentation/filesystems/nfs/nfsroot.txt. + nfsrootdebug [NFS] enable nfsroot debugging messages. + See Documentation/filesystems/nfs/nfsroot.txt. + nfs.callback_tcpport= [NFS] set the TCP port on which the NFSv4 callback channel should listen. diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index 8e7d623173a9..460df3652889 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -67,6 +67,7 @@ * NFS over TCP. * Fabian Frederick: Option parser rebuilt (using parser lib) * Chuck Lever : Use super.c's text-based mount option parsing + * Chuck Lever : Add "nfsrootdebug". */ #include @@ -80,8 +81,6 @@ #include "internal.h" -/* Define this to allow debugging output */ -#undef NFSROOT_DEBUG #define NFSDBG_FACILITY NFSDBG_ROOT /* Default path we try to mount. "%s" gets replaced by our IP address */ @@ -102,6 +101,18 @@ static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = ""; /* server:export path string passed to super.c */ static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = ""; +/* + * When the "nfsrootdebug" kernel command line option is specified, + * enable debugging messages for NFSROOT. + */ +static int __init nfs_root_debug(char *__unused) +{ + nfs_debug |= NFSDBG_ROOT | NFSDBG_MOUNT; + return 1; +} + +__setup("nfsrootdebug", nfs_root_debug); + /* * Parse NFS server and directory information passed on the kernel * command line. @@ -282,10 +293,6 @@ out_devnametoolong: */ int __init nfs_root_data(char **root_device, char **root_data) { -#ifdef NFSROOT_DEBUG - nfs_debug |= NFSDBG_ROOT | NFSDBG_MOUNT; -#endif /* NFSROOT_DEBUG */ - servaddr = root_server_addr; if (servaddr == htonl(INADDR_NONE)) { printk(KERN_ERR "Root-NFS: no NFS server address\n"); -- cgit v1.2.3-70-g09d2 From 8ea13e2c87c83b7cb0b360cb8779415967727647 Mon Sep 17 00:00:00 2001 From: Arnaud Lacombe Date: Mon, 16 Aug 2010 22:55:31 -0400 Subject: kconfig: implement the `mainmenu' directive If specified, the directive must be placed at the top of the Kconfig file. We need to change the grammar to make the mainmenu directive set the `rootmenu' prompt. This reflect how menu_add_prompt() works internally, ie. set the prompt of the `current_entry', pointing originally to `rootmenu'. Signed-off-by: Arnaud Lacombe Reviewed-by: Sam Ravnborg Reviewed-by: Michal Marek --- Documentation/kbuild/kconfig-language.txt | 3 ++- scripts/kconfig/zconf.y | 14 +++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kbuild/kconfig-language.txt b/Documentation/kbuild/kconfig-language.txt index b472e4e0ba67..2fe93ca7c77c 100644 --- a/Documentation/kbuild/kconfig-language.txt +++ b/Documentation/kbuild/kconfig-language.txt @@ -322,7 +322,8 @@ mainmenu: "mainmenu" This sets the config program's title bar if the config program chooses -to use it. +to use it. It should be placed at the top of the configuration, before any +other statement. Kconfig hints diff --git a/scripts/kconfig/zconf.y b/scripts/kconfig/zconf.y index 23dfd3baa7a1..e9b14efd7414 100644 --- a/scripts/kconfig/zconf.y +++ b/scripts/kconfig/zconf.y @@ -36,7 +36,7 @@ static struct menu *current_menu, *current_entry; #define YYERROR_VERBOSE #endif %} -%expect 26 +%expect 28 %union { @@ -104,14 +104,15 @@ static struct menu *current_menu, *current_entry; %} %% -input: stmt_list; +input: nl start | start; + +start: mainmenu_stmt stmt_list | stmt_list; stmt_list: /* empty */ | stmt_list common_stmt | stmt_list choice_stmt | stmt_list menu_stmt - | stmt_list T_MAINMENU prompt nl | stmt_list end { zconf_error("unexpected end statement"); } | stmt_list T_WORD error T_EOL { zconf_error("unknown statement \"%s\"", $2); } | stmt_list option_name error T_EOL @@ -342,6 +343,13 @@ if_block: | if_block choice_stmt ; +/* mainmenu entry */ + +mainmenu_stmt: T_MAINMENU prompt nl +{ + menu_add_prompt(P_MENU, $2, NULL); +}; + /* menu entry */ menu: T_MENU prompt T_EOL -- cgit v1.2.3-70-g09d2 From 0bc79f7f58f5d0670c776818e38f482555bc27ae Mon Sep 17 00:00:00 2001 From: Nikanth Karthikesan Date: Mon, 20 Sep 2010 11:43:58 +0530 Subject: Doc: Fix typo s/packages/packaged Fix typo s/packages/packaged in Documentation/vm/numa_memory_policy.txt. Signed-off-by: Nikanth Karthikesan Signed-off-by: Jiri Kosina --- Documentation/vm/numa_memory_policy.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/vm/numa_memory_policy.txt b/Documentation/vm/numa_memory_policy.txt index 6690fc34ef6d..4e7da6543424 100644 --- a/Documentation/vm/numa_memory_policy.txt +++ b/Documentation/vm/numa_memory_policy.txt @@ -424,7 +424,7 @@ a command line tool, numactl(8), exists that allows one to: + set the shared policy for a shared memory segment via mbind(2) -The numactl(8) tool is packages with the run-time version of the library +The numactl(8) tool is packaged with the run-time version of the library containing the memory policy system call wrappers. Some distributions package the headers and compile-time libraries in a separate development package. -- cgit v1.2.3-70-g09d2 From c67874f942e30039442d925b03793e0a46ddcddd Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 22 Sep 2010 12:55:07 +1000 Subject: nfsd: formally deprecate legacy nfsd syscall interface The syscall interface is has been replaced by a more flexible interface since 2.6.0. It is time to work towards discarding the old interface. So add a entry in feature-removal-schedule.txt and print a warning when the interface is used. Signed-off-by: NeilBrown Signed-off-by: J. Bruce Fields --- Documentation/feature-removal-schedule.txt | 10 ++++++++++ fs/nfsd/nfsctl.c | 10 ++++++++++ 2 files changed, 20 insertions(+) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 842aa9de84a6..076a2c02adaf 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -564,3 +564,13 @@ Who: FUJITA Tomonori ---------------------------- +What: access to nfsd auth cache through sys_nfsservctl or '.' files + in the 'nfsd' filesystem. +When: 2.6.40 +Why: This is a legacy interface which have been replaced by a more + dynamic cache. Continuing to maintain this interface is an + unnecessary burden. +Who: NeilBrown + +---------------------------- + diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index b53b1d042f1f..7f0fc8861b85 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -121,6 +121,16 @@ static ssize_t nfsctl_transaction_write(struct file *file, const char __user *bu static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos) { + static int warned; + if (file->f_dentry->d_name.name[0] == '.' && !warned) { + char name[sizeof(current->comm)]; + printk(KERN_INFO + "Warning: \"%s\" uses deprecated NFSD interface: %s." + " This will be removed in 2.6.40\n", + get_task_comm(name, current), + file->f_dentry->d_name.name); + warned = 1; + } if (! file->private_data) { /* An attempt to read a transaction file without writing * causes a 0-byte write so that the file can return -- cgit v1.2.3-70-g09d2 From 269dcc1c2ec25864308ee03a3fa26ea819d9f5d0 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 7 Sep 2010 14:23:09 -0700 Subject: rcu: Add tracing data to support queueing models The current tracing data is not sufficient to deduce the average time that a callback spends waiting for a grace period to end. Add three per-CPU counters recording the number of callbacks invoked (ci), the number of callbacks orphaned (co), and the number of callbacks adopted (ca). Given the existing callback queue length (ql), the average wait time in absence of CPU hotplug operations is ql/ci. The units of wait time will be in terms of the duration over which ci was measured. In the presence of CPU hotplug operations, there is room for argument, but ql/(ci-co+ca) won't steer you too far wrong. Also fixes a typo called out by Lucas De Marchi . Signed-off-by: Paul E. McKenney --- Documentation/RCU/trace.txt | 13 ++++++++++++- kernel/rcutree.c | 3 +++ kernel/rcutree.h | 3 +++ kernel/rcutree_trace.c | 10 +++++++--- 4 files changed, 25 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/RCU/trace.txt b/Documentation/RCU/trace.txt index efd8cc95c06b..a851118775d8 100644 --- a/Documentation/RCU/trace.txt +++ b/Documentation/RCU/trace.txt @@ -125,6 +125,17 @@ o "b" is the batch limit for this CPU. If more than this number of RCU callbacks is ready to invoke, then the remainder will be deferred. +o "ci" is the number of RCU callbacks that have been invoked for + this CPU. Note that ci+ql is the number of callbacks that have + been registered in absence of CPU-hotplug activity. + +o "co" is the number of RCU callbacks that have been orphaned due to + this CPU going offline. + +o "ca" is the number of RCU callbacks that have been adopted due to + other CPUs going offline. Note that ci+co-ca+ql is the number of + RCU callbacks registered on this CPU. + There is also an rcu/rcudata.csv file with the same information in comma-separated-variable spreadsheet format. @@ -180,7 +191,7 @@ o "s" is the "signaled" state that drives force_quiescent_state()'s o "jfq" is the number of jiffies remaining for this grace period before force_quiescent_state() is invoked to help push things - along. Note that CPUs in dyntick-idle mode thoughout the grace + along. Note that CPUs in dyntick-idle mode throughout the grace period will not report on their own, but rather must be check by some other CPU via force_quiescent_state(). diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 42140a860bb9..e75073504a31 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -1004,6 +1004,7 @@ static void rcu_send_cbs_to_orphanage(struct rcu_state *rsp) for (i = 0; i < RCU_NEXT_SIZE; i++) rdp->nxttail[i] = &rdp->nxtlist; rsp->orphan_qlen += rdp->qlen; + rdp->n_cbs_orphaned += rdp->qlen; rdp->qlen = 0; raw_spin_unlock(&rsp->onofflock); /* irqs remain disabled. */ } @@ -1025,6 +1026,7 @@ static void rcu_adopt_orphan_cbs(struct rcu_state *rsp) *rdp->nxttail[RCU_NEXT_TAIL] = rsp->orphan_cbs_list; rdp->nxttail[RCU_NEXT_TAIL] = rsp->orphan_cbs_tail; rdp->qlen += rsp->orphan_qlen; + rdp->n_cbs_adopted += rsp->orphan_qlen; rsp->orphan_cbs_list = NULL; rsp->orphan_cbs_tail = &rsp->orphan_cbs_list; rsp->orphan_qlen = 0; @@ -1156,6 +1158,7 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp) /* Update count, and requeue any remaining callbacks. */ rdp->qlen -= count; + rdp->n_cbs_invoked += count; if (list != NULL) { *tail = rdp->nxtlist; rdp->nxtlist = list; diff --git a/kernel/rcutree.h b/kernel/rcutree.h index 7918ba61873f..91d4170c5c13 100644 --- a/kernel/rcutree.h +++ b/kernel/rcutree.h @@ -202,6 +202,9 @@ struct rcu_data { long qlen; /* # of queued callbacks */ long qlen_last_fqs_check; /* qlen at last check for QS forcing */ + unsigned long n_cbs_invoked; /* count of RCU cbs invoked. */ + unsigned long n_cbs_orphaned; /* RCU cbs sent to orphanage. */ + unsigned long n_cbs_adopted; /* RCU cbs adopted from orphanage. */ unsigned long n_force_qs_snap; /* did other CPU force QS recently? */ long blimit; /* Upper limit on a processed batch */ diff --git a/kernel/rcutree_trace.c b/kernel/rcutree_trace.c index 458e032a3a30..d15430b9d122 100644 --- a/kernel/rcutree_trace.c +++ b/kernel/rcutree_trace.c @@ -64,7 +64,9 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) rdp->dynticks_fqs); #endif /* #ifdef CONFIG_NO_HZ */ seq_printf(m, " of=%lu ri=%lu", rdp->offline_fqs, rdp->resched_ipi); - seq_printf(m, " ql=%ld b=%ld\n", rdp->qlen, rdp->blimit); + seq_printf(m, " ql=%ld b=%ld", rdp->qlen, rdp->blimit); + seq_printf(m, " ci=%lu co=%lu ca=%lu\n", + rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted); } #define PRINT_RCU_DATA(name, func, m) \ @@ -119,7 +121,9 @@ static void print_one_rcu_data_csv(struct seq_file *m, struct rcu_data *rdp) rdp->dynticks_fqs); #endif /* #ifdef CONFIG_NO_HZ */ seq_printf(m, ",%lu,%lu", rdp->offline_fqs, rdp->resched_ipi); - seq_printf(m, ",%ld,%ld\n", rdp->qlen, rdp->blimit); + seq_printf(m, ",%ld,%ld", rdp->qlen, rdp->blimit); + seq_printf(m, ",%lu,%lu,%lu\n", + rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted); } static int show_rcudata_csv(struct seq_file *m, void *unused) @@ -128,7 +132,7 @@ static int show_rcudata_csv(struct seq_file *m, void *unused) #ifdef CONFIG_NO_HZ seq_puts(m, "\"dt\",\"dt nesting\",\"dn\",\"df\","); #endif /* #ifdef CONFIG_NO_HZ */ - seq_puts(m, "\"of\",\"ri\",\"ql\",\"b\"\n"); + seq_puts(m, "\"of\",\"ri\",\"ql\",\"b\",\"ci\",\"co\",\"ca\"\n"); #ifdef CONFIG_TREE_PREEMPT_RCU seq_puts(m, "\"rcu_preempt:\"\n"); PRINT_RCU_DATA(rcu_preempt_data, print_one_rcu_data_csv, m); -- cgit v1.2.3-70-g09d2 From 47eb1d841790082d2fb4bc7a83c81e0a84a30a9e Mon Sep 17 00:00:00 2001 From: Florian Tobias Schandinat Date: Sun, 19 Sep 2010 05:35:44 +0000 Subject: viafb: add documentation for proc interface This patch adds documentation for the new proc interface that allows modification of the output device configuration. Should be stable and useful enough now for daily use. Signed-off-by: Florian Tobias Schandinat Acked-by: Jonathan Corbet Cc: Joseph Chan Cc: Andrew Morton --- Documentation/fb/viafb.txt | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'Documentation') diff --git a/Documentation/fb/viafb.txt b/Documentation/fb/viafb.txt index f3e046a6a987..1a2e8aa3fbb1 100644 --- a/Documentation/fb/viafb.txt +++ b/Documentation/fb/viafb.txt @@ -197,6 +197,54 @@ Notes: example, # fbset -depth 16 + +[Configure viafb via /proc] +--------------------------- + The following files exist in /proc/viafb + + supported_output_devices + + This read-only file contains a full ',' seperated list containing all + output devices that could be available on your platform. It is likely + that not all of those have a connector on your hardware but it should + provide a good starting point to figure out which of those names match + a real connector. + Example: + # cat /proc/viafb/supported_output_devices + + iga1/output_devices + iga2/output_devices + + These two files are readable and writable. iga1 and iga2 are the two + independent units that produce the screen image. Those images can be + forwarded to one or more output devices. Reading those files is a way + to query which output devices are currently used by an iga. + Example: + # cat /proc/viafb/iga1/output_devices + If there are no output devices printed the output of this iga is lost. + This can happen for example if only one (the other) iga is used. + Writing to these files allows adjusting the output devices during + runtime. One can add new devices, remove existing ones or switch + between igas. Essentially you can write a ',' seperated list of device + names (or a single one) in the same format as the output to those + files. You can add a '+' or '-' as a prefix allowing simple addition + and removal of devices. So a prefix '+' adds the devices from your list + to the already existing ones, '-' removes the listed devices from the + existing ones and if no prefix is given it replaces all existing ones + with the listed ones. If you remove devices they are expected to turn + off. If you add devices that are already part of the other iga they are + removed there and added to the new one. + Examples: + Add CRT as output device to iga1 + # echo +CRT > /proc/viafb/iga1/output_devices + + Remove (turn off) DVP1 and LVDS1 as output devices of iga2 + # echo -DVP1,LVDS1 > /proc/viafb/iga2/output_devices + + Replace all iga1 output devices by CRT + # echo CRT > /proc/viafb/iga1/output_devices + + [Bootup with viafb]: -------------------- Add the following line to your grub.conf: -- cgit v1.2.3-70-g09d2 From 4d443a085de2b6bcae5e0a773c63b8731ff27101 Mon Sep 17 00:00:00 2001 From: Kumar Sanghvi Date: Mon, 27 Sep 2010 19:35:06 +0000 Subject: Documentation: Update Phonet doc for Pipe Controller implementation Updates the Phonet document with description related to Pipe controller implementation Signed-off-by: Kumar Sanghvi Acked-by: Linus Walleij Signed-off-by: David S. Miller --- Documentation/networking/phonet.txt | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'Documentation') diff --git a/Documentation/networking/phonet.txt b/Documentation/networking/phonet.txt index cf76608a2d35..cccf5ff07ec2 100644 --- a/Documentation/networking/phonet.txt +++ b/Documentation/networking/phonet.txt @@ -182,6 +182,59 @@ The pipe protocol provides two socket options at the SOL_PNPIPE level: or zero if encapsulation is off. +Phonet Pipe-controller Implementation +------------------------------------- + +Phonet Pipe-controller is enabled by selecting the CONFIG_PHONET_PIPECTRLR Kconfig +option. It is useful when communicating with those Nokia Modems which do not +implement Pipe controller in them e.g. Nokia Slim Modem used in ST-Ericsson +U8500 platform. + +The implementation is based on the Data Connection Establishment Sequence +depicted in 'Nokia Wireless Modem API - Wireless_modem_user_guide.pdf' +document. + +It allows a phonet sequenced socket (host-pep) to initiate a Pipe connection +between itself and a remote pipe-end point (e.g. modem). + +The implementation adds socket options at SOL_PNPIPE level: + + PNPIPE_CREATE + It accepts an integer argument where-in + lower order 16 bits: pn_dev and pn_port pair for remote pep. + higher order 16 bits: 8 bit pipe-handle + + It sends a PNS_PEP_CONNECT_REQ on sequenced socket itself. On getting + PNS_PEP_CONNECT_RESP, it sends PNS_PEP_CONNECT_REQ to remote pep. On + getting response from remote pep, it selects the best possible Flow + control mechanism supported by remote-pep (modem) and then it sends + PNS_PEP_CREATED_IND to the sequenced socket and to the remote pep. + + It then updates the pipe state associated with the sequenced socket to + be PIPE_DISABLED. + + PNPIPE_ENABLE + It follows the same sequence as above for enabling a pipe by sending + PNS_PEP_ENABLE_REQ initially and then sending PNS_PEP_ENABLED_IND after + getting responses from sequenced socket and remote-pep. + It will also update the pipe state associated with the sequenced socket + to PIPE_ENABLED. + + PNPIPE_DESTROY + This will send out PNS_PEP_DISCONNECT_REQ on the sequenced socket and + the remote pep. + It will also update the pipe state associated with the sequenced socket + to PIPE_IDLE + + PNPIPE_INQ + This getsocktopt allows the user-space running on the sequenced socket + to examine the pipe state associated with that socket ie. whether the + pipe is created (PIPE_DISABLED) or enabled (PIPE_ENABLED) or disabled + (PIPE_DISABLED) or no pipe exists (PIPE_IDLE). + +After a pipe has been created and enabled successfully, the Pipe data can be +exchanged between the host-pep and remote-pep (modem). + Authors ------- -- cgit v1.2.3-70-g09d2 From efdf02cf0651c951de055b0f52128804e11a92fa Mon Sep 17 00:00:00 2001 From: matt mooney Date: Sat, 18 Sep 2010 18:33:57 -0700 Subject: Documentation/kbuild: major edit of modules.txt sections 1-4 Omit needless words and sentences; reorganize and tighten sentence structure; swap sections 2.2 and 2.3 for a more logical flow; remove section 3, therefore shifting 4->3; add to explanations; and add section on building multiple modules. Signed-off-by: matt mooney Signed-off-by: Michal Marek --- Documentation/kbuild/modules.txt | 387 +++++++++++++++++++-------------------- 1 file changed, 189 insertions(+), 198 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kbuild/modules.txt b/Documentation/kbuild/modules.txt index 0767cf69c69e..799b6835993f 100644 --- a/Documentation/kbuild/modules.txt +++ b/Documentation/kbuild/modules.txt @@ -1,215 +1,185 @@ +Building External Modules -In this document you will find information about: -- how to build external modules -- how to make your module use the kbuild infrastructure -- how kbuild will install a kernel -- how to install modules in a non-standard location +This document describes how-to build an out-of-tree kernel module. === Table of Contents === 1 Introduction - === 2 How to build external modules - --- 2.1 Building external modules - --- 2.2 Available targets - --- 2.3 Available options - --- 2.4 Preparing the kernel tree for module build - --- 2.5 Building separate files for a module - === 3. Example commands - === 4. Creating a kbuild file for an external module - === 5. Include files - --- 5.1 How to include files from the kernel include dir - --- 5.2 External modules using an include/ dir - --- 5.3 External modules using several directories - === 6. Module installation - --- 6.1 INSTALL_MOD_PATH - --- 6.2 INSTALL_MOD_DIR - === 7. Module versioning & Module.symvers - --- 7.1 Symbols from the kernel (vmlinux + modules) - --- 7.2 Symbols and external modules - --- 7.3 Symbols from another external module - === 8. Tips & Tricks - --- 8.1 Testing for CONFIG_FOO_BAR + === 2 How-to Build External Modules + --- 2.1 Command Syntax + --- 2.2 Options + --- 2.3 Targets + --- 2.4 Building Separate Files + === 3. Creating a Kbuild File for an External Module + --- 3.1 Shared Makefile + --- 3.2 Separate Kbuild file and Makefile + --- 3.3 Binary Blobs + --- 3.4 Building Multiple Modules + === 4. Include files + --- 4.1 How to include files from the kernel include dir + --- 4.2 External modules using an include/ dir + --- 4.3 External modules using several directories + === 5. Module installation + --- 5.1 INSTALL_MOD_PATH + --- 5.2 INSTALL_MOD_DIR + === 6. Module versioning & Module.symvers + --- 6.1 Symbols from the kernel (vmlinux + modules) + --- 6.2 Symbols and external modules + --- 6.3 Symbols from another external module + === 7. Tips & Tricks + --- 7.1 Testing for CONFIG_FOO_BAR === 1. Introduction -kbuild includes functionality for building modules both -within the kernel source tree and outside the kernel source tree. -The latter is usually referred to as external or "out-of-tree" -modules and is used both during development and for modules that -are not planned to be included in the kernel tree. +"kbuild" is the build system used by the Linux kernel. Modules must use +kbuild to stay compatible with changes in the build infrastructure and +to pick up the right flags to "gcc." Functionality for building modules +both in-tree and out-of-tree is provided. The method for building +either is similar, and all modules are initially developed and built +out-of-tree. -What is covered within this file is mainly information to authors -of modules. The author of an external module should supply -a makefile that hides most of the complexity, so one only has to type -'make' to build the module. A complete example will be presented in -chapter 4, "Creating a kbuild file for an external module". +Covered in this document is information aimed at developers interested +in building out-of-tree (or "external") modules. The author of an +external module should supply a makefile that hides most of the +complexity, so one only has to type "make" to build the module. This is +easily accomplished, and a complete example will be presented in +section 3. -=== 2. How to build external modules +=== 2. How-to Build External Modules -kbuild offers functionality to build external modules, with the -prerequisite that there is a pre-built kernel available with full source. -A subset of the targets available when building the kernel is available -when building an external module. +To build external modules, you must have a pre-built kernel available +that contains the configuration and header files used in the build. +Also, the kernel must have been built with modules enabled. If you are +using a distribution kernel, there will be a package for the kernel you +are running provided by your distribution. ---- 2.1 Building external modules +An alternative is to use the "make" target "modules_prepare." This will +make sure the kernel contains the information required. The target +exists solely as a simple way to prepare a kernel source tree for +building external modules. - Use the following command to build an external module: +NOTE: "modules_prepare" will not build Module.symvers even if +CONFIG_MODVERSIONS is set; therefore, a full kernel build needs to be +executed to make module versioning work. - make -C M=`pwd` +--- 2.1 Command Syntax - For the running kernel use: + The command to build an external module is: - make -C /lib/modules/`uname -r`/build M=`pwd` + make -C M=$PWD - For the above command to succeed, the kernel must have been - built with modules enabled. + The kbuild system knows that an external module is being built + due to the "M=" option given in the command. - To install the modules that were just built: + To build against the running kernel use: - make -C M=`pwd` modules_install + make -C /lib/modules/`uname -r`/build M=$PWD - More complex examples will be shown later, the above should - be enough to get you started. + Then to install the module(s) just built, add the target + "modules_install" to the command: ---- 2.2 Available targets + make -C /lib/modules/`uname -r`/build M=$PWD modules_install - $KDIR refers to the path to the kernel source top-level directory +--- 2.2 Options - make -C $KDIR M=`pwd` - Will build the module(s) located in current directory. - All output files will be located in the same directory - as the module source. - No attempts are made to update the kernel source, and it is - a precondition that a successful make has been executed - for the kernel. + ($KDIR refers to the path of the kernel source directory.) - make -C $KDIR M=`pwd` modules - The modules target is implied when no target is given. - Same functionality as if no target was specified. - See description above. + make -C $KDIR M=$PWD - make -C $KDIR M=`pwd` modules_install - Install the external module(s). - Installation default is in /lib/modules//extra, - but may be prefixed with INSTALL_MOD_PATH - see separate - chapter. + -C $KDIR + The directory where the kernel source is located. + "make" will actually change to the specified directory + when executing and will change back when finished. - make -C $KDIR M=`pwd` clean - Remove all generated files for the module - the kernel - source directory is not modified. + M=$PWD + Informs kbuild that an external module is being built. + The value given to "M" is the absolute path of the + directory where the external module (kbuild file) is + located. - make -C $KDIR M=`pwd` help - help will list the available target when building external - modules. +--- 2.3 Targets ---- 2.3 Available options: + When building an external module, only a subset of the "make" + targets are available. - $KDIR refers to the path to the kernel source top-level directory + make -C $KDIR M=$PWD [target] - make -C $KDIR - Used to specify where to find the kernel source. - '$KDIR' represent the directory where the kernel source is. - Make will actually change directory to the specified directory - when executed but change back when finished. + The default will build the module(s) located in the current + directory, so a target does not need to be specified. All + output files will also be generated in this directory. No + attempts are made to update the kernel source, and it is a + precondition that a successful "make" has been executed for the + kernel. - make -C $KDIR M=`pwd` - M= is used to tell kbuild that an external module is - being built. - The option given to M= is the directory where the external - module (kbuild file) is located. - When an external module is being built only a subset of the - usual targets are available. + modules + The default target for external modules. It has the + same functionality as if no target was specified. See + description above. - make -C $KDIR SUBDIRS=`pwd` - Same as M=. The SUBDIRS= syntax is kept for backwards - compatibility. + modules_install + Install the external module(s). The default location is + /lib/modules//extra, but a prefix may + be added with INSTALL_MOD_PATH (discussed in section 5). ---- 2.4 Preparing the kernel tree for module build + clean + Remove all generated files in the module directory only. - To make sure the kernel contains the information required to - build external modules the target 'modules_prepare' must be used. - 'modules_prepare' exists solely as a simple way to prepare - a kernel source tree for building external modules. - Note: modules_prepare will not build Module.symvers even if - CONFIG_MODVERSIONS is set. Therefore a full kernel build - needs to be executed to make module versioning work. + help + List the available targets for external modules. ---- 2.5 Building separate files for a module - It is possible to build single files which are part of a module. - This works equally well for the kernel, a module and even for - external modules. - Examples (module foo.ko, consist of bar.o, baz.o): - make -C $KDIR M=`pwd` bar.lst - make -C $KDIR M=`pwd` bar.o - make -C $KDIR M=`pwd` foo.ko - make -C $KDIR M=`pwd` / - - -=== 3. Example commands - -This example shows the actual commands to be executed when building -an external module for the currently running kernel. -In the example below, the distribution is supposed to use the -facility to locate output files for a kernel compile in a different -directory than the kernel source - but the examples will also work -when the source and the output files are mixed in the same directory. - -# Kernel source -/lib/modules//source -> /usr/src/linux- +--- 2.4 Building Separate Files -# Output from kernel compile -/lib/modules//build -> /usr/src/linux--up - -Change to the directory where the kbuild file is located and execute -the following commands to build the module: + It is possible to build single files that are part of a module. + This works equally well for the kernel, a module, and even for + external modules. - cd /home/user/src/module - make -C /usr/src/`uname -r`/source \ - O=/lib/modules/`uname-r`/build \ - M=`pwd` + Example (The module foo.ko, consist of bar.o and baz.o): + make -C $KDIR M=$PWD bar.lst + make -C $KDIR M=$PWD baz.o + make -C $KDIR M=$PWD foo.ko + make -C $KDIR M=$PWD / -Then, to install the module use the following command: - make -C /usr/src/`uname -r`/source \ - O=/lib/modules/`uname-r`/build \ - M=`pwd` \ - modules_install +=== 3. Creating a Kbuild File for an External Module -If you look closely you will see that this is the same command as -listed before - with the directories spelled out. +In the last section we saw the command to build a module for the +running kernel. The module is not actually built, however, because a +build file is required. Contained in this file will be the name of +the module(s) being built, along with the list of requisite source +files. The file may be as simple as a single line: -The above are rather long commands, and the following chapter -lists a few tricks to make it all easier. + obj-m := .o +The kbuild system will build .o from .c, +and, after linking, will result in the kernel module .ko. +The above line can be put in either a "Kbuild" file or a "Makefile." +When the module is built from multiple sources, an additional line is +needed listing the files: -=== 4. Creating a kbuild file for an external module + -y := .o .o ... -kbuild is the build system for the kernel, and external modules -must use kbuild to stay compatible with changes in the build system -and to pick up the right flags to gcc etc. +NOTE: Further documentation describing the syntax used by kbuild is +located in Documentation/kbuild/makefiles.txt. -The kbuild file used as input shall follow the syntax described -in Documentation/kbuild/makefiles.txt. This chapter will introduce a few -more tricks to be used when dealing with external modules. +The examples below demonstrate how-to create a build file for the +module 8123.ko, which is built from the following files: -In the following a Makefile will be created for a module with the -following files: 8123_if.c 8123_if.h 8123_pci.c 8123_bin.o_shipped <= Binary blob ---- 4.1 Shared Makefile for module and kernel +--- 3.1 Shared Makefile - An external module always includes a wrapper Makefile supporting - building the module using 'make' with no arguments. - The Makefile provided will most likely include additional - functionality such as test targets etc. and this part shall - be filtered away from kbuild since it may impact kbuild if - name clashes occurs. + An external module always includes a wrapper makefile that + supports building the module using "make" with no arguments. + This target is not used by kbuild; it is only for convenience. + Additional functionality, such as test targets, can be included + but should be filtered out from kbuild due to possible name + clashes. Example 1: --> filename: Makefile @@ -219,11 +189,11 @@ following files: 8123-y := 8123_if.o 8123_pci.o 8123_bin.o else - # Normal Makefile + # normal makefile + KDIR ?= /lib/modules/`uname -r`/build - KERNELDIR := /lib/modules/`uname -r`/build - all:: - $(MAKE) -C $(KERNELDIR) M=`pwd` $@ + default: + $(MAKE) -C $(KDIR) M=$$PWD # Module specific targets genbin: @@ -231,15 +201,20 @@ following files: endif - In example 1, the check for KERNELRELEASE is used to separate - the two parts of the Makefile. kbuild will only see the two - assignments whereas make will see everything except the two - kbuild assignments. + The check for KERNELRELEASE is used to separate the two parts + of the makefile. In the example, kbuild will only see the two + assignments, whereas "make" will see everything except these + two assignments. This is due to two passes made on the file: + the first pass is by the "make" instance run on the + command line; the second pass is by the kbuild system, which is + initiated by the parameterized "make" in the default target. - In recent versions of the kernel, kbuild will look for a file named - Kbuild and as second option look for a file named Makefile. - Utilising the Kbuild file makes us split up the Makefile in example 1 - into two files as shown in example 2: +--- 3.2 Separate Kbuild File and Makefile + + In newer versions of the kernel, kbuild will first look for a + file named "Kbuild", and only if that is not found, will it + then look for a makefile. Utilizing a "Kbuild" file allows us + to split up the makefile from example 1 into two files: Example 2: --> filename: Kbuild @@ -247,20 +222,21 @@ following files: 8123-y := 8123_if.o 8123_pci.o 8123_bin.o --> filename: Makefile - KERNELDIR := /lib/modules/`uname -r`/build - all:: - $(MAKE) -C $(KERNELDIR) M=`pwd` $@ + KDIR ?= /lib/modules/`uname -r`/build + + default: + $(MAKE) -C $(KDIR) M=$$PWD # Module specific targets genbin: echo "X" > 8123_bin.o_shipped + The split in example 2 is questionable due to the simplicity of + each file; however, some external modules use makefiles + consisting of several hundred lines, and here it really pays + off to separate the kbuild part from the rest. - In example 2, we are down to two fairly simple files and for simple - files as used in this example the split is questionable. But some - external modules use Makefiles of several hundred lines and here it - really pays off to separate the kbuild part from the rest. - Example 3 shows a backward compatible version. + The next example shows a backward compatible version. Example 3: --> filename: Kbuild @@ -269,13 +245,15 @@ following files: --> filename: Makefile ifneq ($(KERNELRELEASE),) + # kbuild part of makefile include Kbuild + else - # Normal Makefile + # normal makefile + KDIR ?= /lib/modules/`uname -r`/build - KERNELDIR := /lib/modules/`uname -r`/build - all:: - $(MAKE) -C $(KERNELDIR) M=`pwd` $@ + default: + $(MAKE) -C $(KDIR) M=$$PWD # Module specific targets genbin: @@ -283,28 +261,41 @@ following files: endif - The trick here is to include the Kbuild file from Makefile, so - if an older version of kbuild picks up the Makefile, the Kbuild - file will be included. + Here the "Kbuild" file is included from the makefile. This + allows an older version of kbuild, which only knows of + makefiles, to be used when the "make" and kbuild parts are + split into separate files. ---- 4.2 Binary blobs included in a module +--- 3.3 Binary Blobs - Some external modules needs to include a .o as a blob. kbuild - has support for this, but requires the blob file to be named - _shipped. In our example the blob is named - 8123_bin.o_shipped and when the kbuild rules kick in the file - 8123_bin.o is created as a simple copy off the 8213_bin.o_shipped file - with the _shipped part stripped of the filename. - This allows the 8123_bin.o filename to be used in the assignment to - the module. + Some external modules need to include an object file as a blob. + kbuild has support for this, but requires the blob file to be + named _shipped. When the kbuild rules kick in, a copy + of _shipped is created with _shipped stripped off, + giving us . This shortened filename can be used in + the assignment to the module. + + Throughout this section, 8123_bin.o_shipped has been used to + build the kernel module 8123.ko; it has been included as + 8123_bin.o. - Example 4: - obj-m := 8123.o 8123-y := 8123_if.o 8123_pci.o 8123_bin.o - In example 4, there is no distinction between the ordinary .c/.h files - and the binary file. But kbuild will pick up different rules to create - the .o file. + Although there is no distinction between the ordinary source + files and the binary file, kbuild will pick up different rules + when creating the object file for the module. + +--- 3.4 Building Multiple Modules + + kbuild supports building multiple modules with a single build + file. For example, if you want to build two modules, foo and + bar, the kbuild lines would be: + + obj-m := foo.o bar.o + foo-y := + bar-y := + + It is that simple! === 5. Include files -- cgit v1.2.3-70-g09d2 From 9f02186c236ba4ce11ba4e50d5a756c53d56c29e Mon Sep 17 00:00:00 2001 From: matt mooney Date: Sun, 19 Sep 2010 23:06:36 -0700 Subject: Documentation/kbuild: major edit of modules.txt sections 5-8 A follow-up to my edit of the first 4 sections. Shift sections down by one due to the deletion of section 3; grammar corrections along with some rewording; margin width cleanup; and change EXTRA_CFLAGS -> ccflags-y. Signed-off-by: matt mooney Signed-off-by: Michal Marek --- Documentation/kbuild/modules.txt | 358 +++++++++++++++++++-------------------- 1 file changed, 177 insertions(+), 181 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kbuild/modules.txt b/Documentation/kbuild/modules.txt index 799b6835993f..b572db3c5fee 100644 --- a/Documentation/kbuild/modules.txt +++ b/Documentation/kbuild/modules.txt @@ -15,17 +15,17 @@ This document describes how-to build an out-of-tree kernel module. --- 3.2 Separate Kbuild file and Makefile --- 3.3 Binary Blobs --- 3.4 Building Multiple Modules - === 4. Include files - --- 4.1 How to include files from the kernel include dir - --- 4.2 External modules using an include/ dir - --- 4.3 External modules using several directories - === 5. Module installation + === 4. Include Files + --- 4.1 Kernel Includes + --- 4.2 Single Subdirectory + --- 4.3 Several Subdirectories + === 5. Module Installation --- 5.1 INSTALL_MOD_PATH --- 5.2 INSTALL_MOD_DIR - === 6. Module versioning & Module.symvers - --- 6.1 Symbols from the kernel (vmlinux + modules) - --- 6.2 Symbols and external modules - --- 6.3 Symbols from another external module + === 6. Module Versioning + --- 6.1 Symbols From the Kernel (vmlinux + modules) + --- 6.2 Symbols and External Modules + --- 6.3 Symbols From Another External Module === 7. Tips & Tricks --- 7.1 Testing for CONFIG_FOO_BAR @@ -298,236 +298,232 @@ module 8123.ko, which is built from the following files: It is that simple! -=== 5. Include files +=== 4. Include Files -Include files are a necessity when a .c file uses something from other .c -files (not strictly in the sense of C, but if good programming practice is -used). Any module that consists of more than one .c file will have a .h file -for one of the .c files. +Within the kernel, header files are kept in standard locations +according to the following rule: -- If the .h file only describes a module internal interface, then the .h file - shall be placed in the same directory as the .c files. -- If the .h files describe an interface used by other parts of the kernel - located in different directories, the .h files shall be located in - include/linux/ or other include/ directories as appropriate. + * If the header file only describes the internal interface of a + module, then the file is placed in the same directory as the + source files. + * If the header file describes an interface used by other parts + of the kernel that are located in different directories, then + the file is placed in include/linux/. -One exception for this rule is larger subsystems that have their own directory -under include/ such as include/scsi. Another exception is arch-specific -.h files which are located under include/asm-$(ARCH)/*. + NOTE: There are two notable exceptions to this rule: larger + subsystems have their own directory under include/, such as + include/scsi; and architecture specific headers are located + under arch/$(ARCH)/include/. -External modules have a tendency to locate include files in a separate include/ -directory and therefore need to deal with this in their kbuild file. +--- 4.1 Kernel Includes ---- 5.1 How to include files from the kernel include dir - - When a module needs to include a file from include/linux/, then one - just uses: + To include a header file located under include/linux/, simply + use: #include - kbuild will make sure to add options to gcc so the relevant - directories are searched. - Likewise for .h files placed in the same directory as the .c file. - - #include "8123_if.h" - - will do the job. + kbuild will add options to "gcc" so the relevant directories + are searched. ---- 5.2 External modules using an include/ dir +--- 4.2 Single Subdirectory - External modules often locate their .h files in a separate include/ - directory although this is not usual kernel style. When an external - module uses an include/ dir then kbuild needs to be told so. - The trick here is to use either EXTRA_CFLAGS (take effect for all .c - files) or CFLAGS_$F.o (take effect only for a single file). + External modules tend to place header files in a separate + include/ directory where their source is located, although this + is not the usual kernel style. To inform kbuild of the + directory use either ccflags-y or CFLAGS_.o. - In our example, if we move 8123_if.h to a subdirectory named include/ - the resulting Kbuild file would look like: + Using the example from section 3, if we moved 8123_if.h to a + subdirectory named include, the resulting kbuild file would + look like: --> filename: Kbuild - obj-m := 8123.o + obj-m := 8123.o - EXTRA_CFLAGS := -Iinclude + ccflags-y := -Iinclude 8123-y := 8123_if.o 8123_pci.o 8123_bin.o - Note that in the assignment there is no space between -I and the path. - This is a kbuild limitation: there must be no space present. - ---- 5.3 External modules using several directories + Note that in the assignment there is no space between -I and + the path. This is a limitation of kbuild: there must be no + space present. - If an external module does not follow the usual kernel style, but - decides to spread files over several directories, then kbuild can - handle this too. +--- 4.3 Several Subdirectories + kbuild can handle files that are spread over several directories. Consider the following example: - | - +- src/complex_main.c - | +- hal/hardwareif.c - | +- hal/include/hardwareif.h - +- include/complex.h - - To build a single module named complex.ko, we then need the following + . + |__ src + | |__ complex_main.c + | |__ hal + | |__ hardwareif.c + | |__ include + | |__ hardwareif.h + |__ include + |__ complex.h + + To build the module complex.ko, we then need the following kbuild file: - Kbuild: + --> filename: Kbuild obj-m := complex.o complex-y := src/complex_main.o complex-y += src/hal/hardwareif.o - EXTRA_CFLAGS := -I$(src)/include - EXTRA_CFLAGS += -I$(src)src/hal/include + ccflags-y := -I$(src)/include + ccflags-y += -I$(src)/src/hal/include + As you can see, kbuild knows how to handle object files located + in other directories. The trick is to specify the directory + relative to the kbuild file's location. That being said, this + is NOT recommended practice. - kbuild knows how to handle .o files located in another directory - - although this is NOT recommended practice. The syntax is to specify - the directory relative to the directory where the Kbuild file is - located. + For the header files, kbuild must be explicitly told where to + look. When kbuild executes, the current directory is always the + root of the kernel tree (the argument to "-C") and therefore an + absolute path is needed. $(src) provides the absolute path by + pointing to the directory where the currently executing kbuild + file is located. - To find the .h files, we have to explicitly tell kbuild where to look - for the .h files. When kbuild executes, the current directory is always - the root of the kernel tree (argument to -C) and therefore we have to - tell kbuild how to find the .h files using absolute paths. - $(src) will specify the absolute path to the directory where the - Kbuild file are located when being build as an external module. - Therefore -I$(src)/ is used to point out the directory of the Kbuild - file and any additional path are just appended. -=== 6. Module installation +=== 5. Module Installation -Modules which are included in the kernel are installed in the directory: +Modules which are included in the kernel are installed in the +directory: /lib/modules/$(KERNELRELEASE)/kernel -External modules are installed in the directory: +And external modules are installed in: /lib/modules/$(KERNELRELEASE)/extra ---- 6.1 INSTALL_MOD_PATH +--- 5.1 INSTALL_MOD_PATH - Above are the default directories, but as always, some level of - customization is possible. One can prefix the path using the variable - INSTALL_MOD_PATH: + Above are the default directories but as always some level of + customization is possible. A prefix can be added to the + installation path using the variable INSTALL_MOD_PATH: $ make INSTALL_MOD_PATH=/frodo modules_install => Install dir: /frodo/lib/modules/$(KERNELRELEASE)/kernel - INSTALL_MOD_PATH may be set as an ordinary shell variable or as in the - example above, can be specified on the command line when calling make. - INSTALL_MOD_PATH has effect both when installing modules included in - the kernel as well as when installing external modules. + INSTALL_MOD_PATH may be set as an ordinary shell variable or, + as shown above, can be specified on the command line when + calling "make." This has effect when installing both in-tree + and out-of-tree modules. ---- 6.2 INSTALL_MOD_DIR +--- 5.2 INSTALL_MOD_DIR - When installing external modules they are by default installed to a - directory under /lib/modules/$(KERNELRELEASE)/extra, but one may wish - to locate modules for a specific functionality in a separate - directory. For this purpose, one can use INSTALL_MOD_DIR to specify an - alternative name to 'extra'. + External modules are by default installed to a directory under + /lib/modules/$(KERNELRELEASE)/extra, but you may wish to locate + modules for a specific functionality in a separate directory. + For this purpose, use INSTALL_MOD_DIR to specify an alternative + name to "extra." - $ make INSTALL_MOD_DIR=gandalf -C KERNELDIR \ - M=`pwd` modules_install + $ make INSTALL_MOD_DIR=gandalf -C $KDIR \ + M=$PWD modules_install => Install dir: /lib/modules/$(KERNELRELEASE)/gandalf -=== 7. Module versioning & Module.symvers +=== 6. Module Versioning -Module versioning is enabled by the CONFIG_MODVERSIONS tag. +Module versioning is enabled by the CONFIG_MODVERSIONS tag, and is used +as a simple ABI consistency check. A CRC value of the full prototype +for an exported symbol is created. When a module is loaded/used, the +CRC values contained in the kernel are compared with similar values in +the module; if they are not equal, the kernel refuses to load the +module. -Module versioning is used as a simple ABI consistency check. The Module -versioning creates a CRC value of the full prototype for an exported symbol and -when a module is loaded/used then the CRC values contained in the kernel are -compared with similar values in the module. If they are not equal, then the -kernel refuses to load the module. +Module.symvers contains a list of all exported symbols from a kernel +build. -Module.symvers contains a list of all exported symbols from a kernel build. +--- 6.1 Symbols From the Kernel (vmlinux + modules) ---- 7.1 Symbols from the kernel (vmlinux + modules) - - During a kernel build, a file named Module.symvers will be generated. - Module.symvers contains all exported symbols from the kernel and - compiled modules. For each symbols, the corresponding CRC value - is stored too. + During a kernel build, a file named Module.symvers will be + generated. Module.symvers contains all exported symbols from + the kernel and compiled modules. For each symbol, the + corresponding CRC value is also stored. The syntax of the Module.symvers file is: - - Sample: + + 0x2d036834 scsi_remove_host drivers/scsi/scsi_mod - For a kernel build without CONFIG_MODVERSIONS enabled, the crc - would read: 0x00000000 + For a kernel build without CONFIG_MODVERSIONS enabled, the CRC + would read 0x00000000. Module.symvers serves two purposes: - 1) It lists all exported symbols both from vmlinux and all modules - 2) It lists the CRC if CONFIG_MODVERSIONS is enabled - ---- 7.2 Symbols and external modules - - When building an external module, the build system needs access to - the symbols from the kernel to check if all external symbols are - defined. This is done in the MODPOST step and to obtain all - symbols, modpost reads Module.symvers from the kernel. - If a Module.symvers file is present in the directory where - the external module is being built, this file will be read too. - During the MODPOST step, a new Module.symvers file will be written - containing all exported symbols that were not defined in the kernel. - ---- 7.3 Symbols from another external module - - Sometimes, an external module uses exported symbols from another - external module. Kbuild needs to have full knowledge on all symbols - to avoid spitting out warnings about undefined symbols. - Three solutions exist to let kbuild know all symbols of more than - one external module. - The method with a top-level kbuild file is recommended but may be - impractical in certain situations. - - Use a top-level Kbuild file - If you have two modules: 'foo' and 'bar', and 'foo' needs - symbols from 'bar', then one can use a common top-level kbuild - file so both modules are compiled in same build. - - Consider following directory layout: - ./foo/ <= contains the foo module - ./bar/ <= contains the bar module - The top-level Kbuild file would then look like: - - #./Kbuild: (this file may also be named Makefile) + 1) It lists all exported symbols from vmlinux and all modules. + 2) It lists the CRC if CONFIG_MODVERSIONS is enabled. + +--- 6.2 Symbols and External Modules + + When building an external module, the build system needs access + to the symbols from the kernel to check if all external symbols + are defined. This is done in the MODPOST step. modpost obtains + the symbols by reading Module.symvers from the kernel source + tree. If a Module.symvers file is present in the directory + where the external module is being built, this file will be + read too. During the MODPOST step, a new Module.symvers file + will be written containing all exported symbols that were not + defined in the kernel. + +--- 6.3 Symbols From Another External Module + + Sometimes, an external module uses exported symbols from + another external module. kbuild needs to have full knowledge of + all symbols to avoid spitting out warnings about undefined + symbols. Three solutions exist for this situation. + + NOTE: The method with a top-level kbuild file is recommended + but may be impractical in certain situations. + + Use a top-level kbuild file + If you have two modules, foo.ko and bar.ko, where + foo.ko needs symbols from bar.ko, then you can use a + common top-level kbuild file so both modules are + compiled in the same build. Consider following + directory layout: + + ./foo/ <= contains foo.ko + ./bar/ <= contains bar.ko + + The top-level kbuild file would then look like: + + #./Kbuild (or ./Makefile): obj-y := foo/ bar/ - Executing: - make -C $KDIR M=`pwd` + And executing: + $ make -C $KDIR M=$PWD - will then do the expected and compile both modules with full - knowledge on symbols from both modules. + Will then do the expected and compile both modules with + full knowledge of symbols from either module. Use an extra Module.symvers file - When an external module is built, a Module.symvers file is - generated containing all exported symbols which are not - defined in the kernel. - To get access to symbols from module 'bar', one can copy the - Module.symvers file from the compilation of the 'bar' module - to the directory where the 'foo' module is built. - During the module build, kbuild will read the Module.symvers - file in the directory of the external module and when the - build is finished, a new Module.symvers file is created - containing the sum of all symbols defined and not part of the - kernel. - - Use make variable KBUILD_EXTRA_SYMBOLS in the Makefile - If it is impractical to copy Module.symvers from another - module, you can assign a space separated list of files to - KBUILD_EXTRA_SYMBOLS in your Makfile. These files will be - loaded by modpost during the initialisation of its symbol - tables. - -=== 8. Tips & Tricks - ---- 8.1 Testing for CONFIG_FOO_BAR - - Modules often need to check for certain CONFIG_ options to decide if - a specific feature shall be included in the module. When kbuild is used - this is done by referencing the CONFIG_ variable directly. + When an external module is built, a Module.symvers file + is generated containing all exported symbols which are + not defined in the kernel. To get access to symbols + from bar.ko, copy the Module.symvers file from the + compilation of bar.ko to the directory where foo.ko is + built. During the module build, kbuild will read the + Module.symvers file in the directory of the external + module, and when the build is finished, a new + Module.symvers file is created containing the sum of + all symbols defined and not part of the kernel. + + Use "make" variable KBUILD_EXTRA_SYMBOLS + If it is impractical to copy Module.symvers from + another module, you can assign a space separated list + of files to KBUILD_EXTRA_SYMBOLS in your build + file. These files will be loaded by modpost during the + initialization of its symbol tables. + +=== 7. Tips & Tricks + +--- 7.1 Testing for CONFIG_FOO_BAR + + Modules often need to check for certain CONFIG_ options to + decide if a specific feature is included in the module. In + kbuild this is done by referencing the CONFIG_ variable + directly. #fs/ext2/Makefile obj-$(CONFIG_EXT2_FS) += ext2.o @@ -535,9 +531,9 @@ Module.symvers contains a list of all exported symbols from a kernel build. ext2-y := balloc.o bitmap.o dir.o ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o - External modules have traditionally used grep to check for specific - CONFIG_ settings directly in .config. This usage is broken. - As introduced before, external modules shall use kbuild when building - and therefore can use the same methods as in-kernel modules when - testing for CONFIG_ definitions. + External modules have traditionally used "grep" to check for + specific CONFIG_ settings directly in .config. This usage is + broken. As introduced before, external modules should use + kbuild for building and can therefore use the same methods as + in-tree modules when testing for CONFIG_ definitions. -- cgit v1.2.3-70-g09d2 From cdb138080b78146d1cdadba9f5dadbeb97445b91 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 28 Jul 2010 10:59:06 +0200 Subject: pcmcia: do not use win_req_t when calling pcmcia_request_window() Instead of win_req_t, drivers are now requested to fill out struct pcmcia_device *p_dev->resource[2,3,4,5] for up to four iomem ranges. After a call to pcmcia_request_window(), the windows found there are reserved and may be used until pcmcia_release_window() is called. CC: netdev@vger.kernel.org CC: linux-wireless@vger.kernel.org CC: linux-mtd@lists.infradead.org CC: Jiri Kosina CC: linux-scsi@vger.kernel.org Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- Documentation/pcmcia/driver-changes.txt | 7 +++ drivers/char/pcmcia/ipwireless/main.c | 87 ++++++++++++++------------------- drivers/char/pcmcia/ipwireless/main.h | 4 -- drivers/mtd/maps/pcmciamtd.c | 53 +++++++++++--------- drivers/net/pcmcia/fmvj18x_cs.c | 29 +++++------ drivers/net/pcmcia/ibmtr_cs.c | 42 ++++++++-------- drivers/net/pcmcia/pcnet_cs.c | 44 ++++++++--------- drivers/net/pcmcia/smc91c92_cs.c | 15 +++--- drivers/net/pcmcia/xirc2ps_cs.c | 16 +++--- drivers/net/wireless/b43/pcmcia.c | 14 +++--- drivers/net/wireless/ray_cs.c | 44 ++++++++--------- drivers/net/wireless/ray_cs.h | 2 - drivers/pcmcia/pcmcia_resource.c | 60 +++++++++++++---------- drivers/scsi/pcmcia/nsp_cs.c | 49 +++++++------------ include/pcmcia/cs.h | 24 --------- include/pcmcia/ds.h | 28 ++++++++--- 16 files changed, 241 insertions(+), 277 deletions(-) (limited to 'Documentation') diff --git a/Documentation/pcmcia/driver-changes.txt b/Documentation/pcmcia/driver-changes.txt index 26c0f9c00545..86e0f491f85e 100644 --- a/Documentation/pcmcia/driver-changes.txt +++ b/Documentation/pcmcia/driver-changes.txt @@ -1,4 +1,11 @@ This file details changes in 2.6 which affect PCMCIA card driver authors: +* pcmcia_request_window changes (as of 2.6.36) + Instead of win_req_t, drivers are now requested to fill out + struct pcmcia_device *p_dev->resource[2,3,4,5] for up to four ioport + ranges. After a call to pcmcia_request_window(), the regions found there + are reserved and may be used immediately -- until pcmcia_release_window() + is called. + * pcmcia_request_io changes (as of 2.6.36) Instead of io_req_t, drivers are now requested to fill out struct pcmcia_device *p_dev->resource[0,1] for up to two ioport diff --git a/drivers/char/pcmcia/ipwireless/main.c b/drivers/char/pcmcia/ipwireless/main.c index 67bdb05798b1..8d2b86aab715 100644 --- a/drivers/char/pcmcia/ipwireless/main.c +++ b/drivers/char/pcmcia/ipwireless/main.c @@ -105,62 +105,54 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, if (cfg->mem.nwin == 0) return 0; - ipw->request_common_memory.Attributes = + p_dev->resource[2]->flags |= WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM | WIN_ENABLE; - ipw->request_common_memory.Base = cfg->mem.win[0].host_addr; - ipw->request_common_memory.Size = cfg->mem.win[0].len; - if (ipw->request_common_memory.Size < 0x1000) - ipw->request_common_memory.Size = 0x1000; - ipw->request_common_memory.AccessSpeed = 0; - - ret = pcmcia_request_window(p_dev, &ipw->request_common_memory, - &ipw->handle_common_memory); + p_dev->resource[2]->start = cfg->mem.win[0].host_addr; + p_dev->resource[2]->end = cfg->mem.win[0].len; + if (p_dev->resource[2]->end < 0x1000) + p_dev->resource[2]->end = 0x1000; + ret = pcmcia_request_window(p_dev, p_dev->resource[2], 0); if (ret != 0) goto exit1; - ret = pcmcia_map_mem_page(p_dev, ipw->handle_common_memory, + ret = pcmcia_map_mem_page(p_dev, p_dev->resource[2], cfg->mem.win[0].card_addr); - if (ret != 0) goto exit2; ipw->is_v2_card = cfg->mem.win[0].len == 0x100; - ipw->common_memory = ioremap(ipw->request_common_memory.Base, - ipw->request_common_memory.Size); - request_mem_region(ipw->request_common_memory.Base, - ipw->request_common_memory.Size, + ipw->attr_memory = ioremap(p_dev->resource[2]->start, + resource_size(p_dev->resource[2])); + request_mem_region(p_dev->resource[2]->start, + resource_size(p_dev->resource[2]), IPWIRELESS_PCCARD_NAME); - ipw->request_attr_memory.Attributes = - WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_AM | WIN_ENABLE; - ipw->request_attr_memory.Base = 0; - ipw->request_attr_memory.Size = 0; /* this used to be 0x1000 */ - ipw->request_attr_memory.AccessSpeed = 0; - - ret = pcmcia_request_window(p_dev, &ipw->request_attr_memory, - &ipw->handle_attr_memory); - + p_dev->resource[3]->flags |= WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_AM | + WIN_ENABLE; + p_dev->resource[3]->end = 0; /* this used to be 0x1000 */ + ret = pcmcia_request_window(p_dev, p_dev->resource[3], 0); if (ret != 0) goto exit2; - ret = pcmcia_map_mem_page(p_dev, ipw->handle_attr_memory, 0); + ret = pcmcia_map_mem_page(p_dev, p_dev->resource[3], 0); if (ret != 0) goto exit3; - ipw->attr_memory = ioremap(ipw->request_attr_memory.Base, - ipw->request_attr_memory.Size); - request_mem_region(ipw->request_attr_memory.Base, - ipw->request_attr_memory.Size, IPWIRELESS_PCCARD_NAME); + ipw->attr_memory = ioremap(p_dev->resource[3]->start, + resource_size(p_dev->resource[3])); + request_mem_region(p_dev->resource[3]->start, + resource_size(p_dev->resource[3]), + IPWIRELESS_PCCARD_NAME); return 0; exit3: exit2: if (ipw->common_memory) { - release_mem_region(ipw->request_common_memory.Base, - ipw->request_common_memory.Size); + release_mem_region(p_dev->resource[2]->start, + resource_size(p_dev->resource[2])); iounmap(ipw->common_memory); } exit1: @@ -201,13 +193,9 @@ static int config_ipwireless(struct ipw_dev *ipw) (unsigned int) link->irq); if (ipw->attr_memory && ipw->common_memory) printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": attr memory 0x%08lx-0x%08lx, common memory 0x%08lx-0x%08lx\n", - ipw->request_attr_memory.Base, - ipw->request_attr_memory.Base - + ipw->request_attr_memory.Size - 1, - ipw->request_common_memory.Base, - ipw->request_common_memory.Base - + ipw->request_common_memory.Size - 1); + ": attr memory %pR, common memory %pR\n", + link->resource[3], + link->resource[2]); ipw->network = ipwireless_network_create(ipw->hardware); if (!ipw->network) @@ -231,17 +219,16 @@ static int config_ipwireless(struct ipw_dev *ipw) return 0; exit: - if (ipw->attr_memory) { - release_mem_region(ipw->request_attr_memory.Base, - ipw->request_attr_memory.Size); - iounmap(ipw->attr_memory); - - } if (ipw->common_memory) { - release_mem_region(ipw->request_common_memory.Base, - ipw->request_common_memory.Size); + release_mem_region(link->resource[2]->start, + resource_size(link->resource[2])); iounmap(ipw->common_memory); } + if (ipw->attr_memory) { + release_mem_region(link->resource[3]->start, + resource_size(link->resource[3])); + iounmap(ipw->attr_memory); + } pcmcia_disable_device(link); return -1; } @@ -249,13 +236,13 @@ exit: static void release_ipwireless(struct ipw_dev *ipw) { if (ipw->common_memory) { - release_mem_region(ipw->request_common_memory.Base, - ipw->request_common_memory.Size); + release_mem_region(ipw->link->resource[2]->start, + resource_size(ipw->link->resource[2])); iounmap(ipw->common_memory); } if (ipw->attr_memory) { - release_mem_region(ipw->request_attr_memory.Base, - ipw->request_attr_memory.Size); + release_mem_region(ipw->link->resource[3]->start, + resource_size(ipw->link->resource[3])); iounmap(ipw->attr_memory); } pcmcia_disable_device(ipw->link); diff --git a/drivers/char/pcmcia/ipwireless/main.h b/drivers/char/pcmcia/ipwireless/main.h index c207be87b597..90402195855e 100644 --- a/drivers/char/pcmcia/ipwireless/main.h +++ b/drivers/char/pcmcia/ipwireless/main.h @@ -45,13 +45,9 @@ struct ipw_dev { struct pcmcia_device *link; int is_v2_card; - window_handle_t handle_attr_memory; void __iomem *attr_memory; - win_req_t request_attr_memory; - window_handle_t handle_common_memory; void __iomem *common_memory; - win_req_t request_common_memory; /* Reference to attribute memory, containing CIS data */ void *attribute_memory; diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index e9ca5ba7d9d2..fb3c5380aa84 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -101,7 +101,7 @@ MODULE_PARM_DESC(mem_type, "Set Memory type (0=Flash, 1=RAM, 2=ROM, default=0)") static caddr_t remap_window(struct map_info *map, unsigned long to) { struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; - window_handle_t win = (window_handle_t)map->map_priv_2; + struct resource *win = (struct resource *) map->map_priv_2; unsigned int offset; int ret; @@ -339,7 +339,7 @@ static void pcmciamtd_release(struct pcmcia_device *link) DEBUG(3, "link = 0x%p", link); - if (link->win) { + if (link->resource[2]->end) { if(dev->win_base) { iounmap(dev->win_base); dev->win_base = NULL; @@ -491,9 +491,8 @@ static int pcmciamtd_config(struct pcmcia_device *link) { struct pcmciamtd_dev *dev = link->priv; struct mtd_info *mtd = NULL; - win_req_t req; int ret; - int i; + int i, j = 0; static char *probes[] = { "jedec_probe", "cfi_probe" }; int new_name = 0; @@ -520,28 +519,34 @@ static int pcmciamtd_config(struct pcmcia_device *link) * smaller windows until we succeed */ - req.Attributes = WIN_MEMORY_TYPE_CM | WIN_ENABLE; - req.Attributes |= (dev->pcmcia_map.bankwidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; - req.Base = 0; - req.AccessSpeed = mem_speed; - link->win = (window_handle_t)link; - req.Size = (force_size) ? force_size << 20 : MAX_PCMCIA_ADDR; + link->resource[2]->flags |= WIN_MEMORY_TYPE_CM | WIN_ENABLE; + link->resource[2]->flags |= (dev->pcmcia_map.bankwidth == 1) ? + WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; + link->resource[2]->start = 0; + link->resource[2]->end = (force_size) ? force_size << 20 : + MAX_PCMCIA_ADDR; dev->win_size = 0; do { int ret; - DEBUG(2, "requesting window with size = %dKiB memspeed = %d", - req.Size >> 10, req.AccessSpeed); - ret = pcmcia_request_window(link, &req, &link->win); + DEBUG(2, "requesting window with size = %luKiB memspeed = %d", + (unsigned long) resource_size(link->resource[2]) >> 10, + mem_speed); + ret = pcmcia_request_window(link, link->resource[2], mem_speed); DEBUG(2, "ret = %d dev->win_size = %d", ret, dev->win_size); if(ret) { - req.Size >>= 1; + j++; + link->resource[2]->start = 0; + link->resource[2]->end = (force_size) ? + force_size << 20 : MAX_PCMCIA_ADDR; + link->resource[2]->end >>= j; } else { - DEBUG(2, "Got window of size %dKiB", req.Size >> 10); - dev->win_size = req.Size; + DEBUG(2, "Got window of size %luKiB", (unsigned long) + resource_size(link->resource[2]) >> 10); + dev->win_size = resource_size(link->resource[2]); break; } - } while(req.Size >= 0x1000); + } while (link->resource[2]->end >= 0x1000); DEBUG(2, "dev->win_size = %d", dev->win_size); @@ -553,20 +558,20 @@ static int pcmciamtd_config(struct pcmcia_device *link) DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10); /* Get write protect status */ - DEBUG(2, "window handle = 0x%8.8lx", (unsigned long)link->win); - dev->win_base = ioremap(req.Base, req.Size); + dev->win_base = ioremap(link->resource[2]->start, + resource_size(link->resource[2])); if(!dev->win_base) { - dev_err(&dev->p_dev->dev, "ioremap(%lu, %u) failed\n", - req.Base, req.Size); + dev_err(&dev->p_dev->dev, "ioremap(%pR) failed\n", + link->resource[2]); pcmciamtd_release(link); return -ENODEV; } - DEBUG(1, "mapped window dev = %p req.base = 0x%lx base = %p size = 0x%x", - dev, req.Base, dev->win_base, req.Size); + DEBUG(1, "mapped window dev = %p @ %pR, base = %p", + dev, link->resource[2], dev->win_base); dev->offset = 0; dev->pcmcia_map.map_priv_1 = (unsigned long)dev; - dev->pcmcia_map.map_priv_2 = (unsigned long)link->win; + dev->pcmcia_map.map_priv_2 = (unsigned long)link->resource[2]; dev->vpp = (vpp) ? vpp : link->socket->socket.Vpp; link->conf.Attributes = 0; diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c index 98fffb03ecd7..dfd32842412e 100644 --- a/drivers/net/pcmcia/fmvj18x_cs.c +++ b/drivers/net/pcmcia/fmvj18x_cs.c @@ -544,20 +544,18 @@ failed: static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id) { - win_req_t req; u_char __iomem *base; int i, j; /* Allocate a small memory window */ - req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; - req.Base = 0; req.Size = 0; - req.AccessSpeed = 0; - i = pcmcia_request_window(link, &req, &link->win); + link->resource[2]->flags |= WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; + link->resource[2]->start = 0; link->resource[2]->end = 0; + i = pcmcia_request_window(link, link->resource[2], 0); if (i != 0) return -1; - base = ioremap(req.Base, req.Size); - pcmcia_map_mem_page(link, link->win, 0); + base = ioremap(link->resource[2]->start, resource_size(link->resource[2])); + pcmcia_map_mem_page(link, link->resource[2], 0); /* * MBH10304 CISTPL_FUNCE_LAN_NODE_ID format @@ -582,7 +580,7 @@ static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id) } iounmap(base); - j = pcmcia_release_window(link, link->win); + j = pcmcia_release_window(link, link->resource[2]); return (i != 0x200) ? 0 : -1; } /* fmvj18x_get_hwinfo */ @@ -590,27 +588,26 @@ static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id) static int fmvj18x_setup_mfc(struct pcmcia_device *link) { - win_req_t req; int i; struct net_device *dev = link->priv; unsigned int ioaddr; local_info_t *lp = netdev_priv(dev); /* Allocate a small memory window */ - req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; - req.Base = 0; req.Size = 0; - req.AccessSpeed = 0; - i = pcmcia_request_window(link, &req, &link->win); + link->resource[3]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; + link->resource[3]->start = link->resource[3]->end = 0; + i = pcmcia_request_window(link, link->resource[3], 0); if (i != 0) return -1; - lp->base = ioremap(req.Base, req.Size); + lp->base = ioremap(link->resource[3]->start, + resource_size(link->resource[3])); if (lp->base == NULL) { printk(KERN_NOTICE "fmvj18x_cs: ioremap failed\n"); return -1; } - i = pcmcia_map_mem_page(link, link->win, 0); + i = pcmcia_map_mem_page(link, link->resource[3], 0); if (i != 0) { iounmap(lp->base); lp->base = NULL; @@ -638,7 +635,6 @@ static void fmvj18x_release(struct pcmcia_device *link) struct net_device *dev = link->priv; local_info_t *lp = netdev_priv(dev); u_char __iomem *tmp; - int j; dev_dbg(&link->dev, "fmvj18x_release\n"); @@ -646,7 +642,6 @@ static void fmvj18x_release(struct pcmcia_device *link) tmp = lp->base; lp->base = NULL; /* set NULL before iounmap */ iounmap(tmp); - j = pcmcia_release_window(link, link->win); } pcmcia_disable_device(link); diff --git a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/pcmcia/ibmtr_cs.c index b0d06a3d962f..dbdea7f5e423 100644 --- a/drivers/net/pcmcia/ibmtr_cs.c +++ b/drivers/net/pcmcia/ibmtr_cs.c @@ -102,9 +102,8 @@ static void ibmtr_detach(struct pcmcia_device *p_dev); typedef struct ibmtr_dev_t { struct pcmcia_device *p_dev; - struct net_device *dev; - window_handle_t sram_win_handle; - struct tok_info *ti; + struct net_device *dev; + struct tok_info *ti; } ibmtr_dev_t; static void netdev_get_drvinfo(struct net_device *dev, @@ -210,7 +209,6 @@ static int __devinit ibmtr_config(struct pcmcia_device *link) ibmtr_dev_t *info = link->priv; struct net_device *dev = info->dev; struct tok_info *ti = netdev_priv(dev); - win_req_t req; int i, ret; dev_dbg(&link->dev, "ibmtr_config\n"); @@ -240,37 +238,37 @@ static int __devinit ibmtr_config(struct pcmcia_device *link) ti->global_int_enable=GLOBAL_INT_ENABLE+((dev->irq==9) ? 2 : dev->irq); /* Allocate the MMIO memory window */ - req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE; - req.Attributes |= WIN_USE_WAIT; - req.Base = 0; - req.Size = 0x2000; - req.AccessSpeed = 250; - ret = pcmcia_request_window(link, &req, &link->win); + link->resource[2]->flags |= WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE; + link->resource[2]->flags |= WIN_USE_WAIT; + link->resource[2]->start = 0; + link->resource[2]->end = 0x2000; + ret = pcmcia_request_window(link, link->resource[2], 250); if (ret) goto failed; - ret = pcmcia_map_mem_page(link, link->win, mmiobase); + ret = pcmcia_map_mem_page(link, link->resource[2], mmiobase); if (ret) goto failed; - ti->mmio = ioremap(req.Base, req.Size); + ti->mmio = ioremap(link->resource[2]->start, + resource_size(link->resource[2])); /* Allocate the SRAM memory window */ - req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE; - req.Attributes |= WIN_USE_WAIT; - req.Base = 0; - req.Size = sramsize * 1024; - req.AccessSpeed = 250; - ret = pcmcia_request_window(link, &req, &info->sram_win_handle); + link->resource[3]->flags = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE; + link->resource[3]->flags |= WIN_USE_WAIT; + link->resource[3]->start = 0; + link->resource[3]->end = sramsize * 1024; + ret = pcmcia_request_window(link, link->resource[3], 250); if (ret) goto failed; - ret = pcmcia_map_mem_page(link, info->sram_win_handle, srambase); + ret = pcmcia_map_mem_page(link, link->resource[3], srambase); if (ret) goto failed; ti->sram_base = srambase >> 12; - ti->sram_virt = ioremap(req.Base, req.Size); - ti->sram_phys = req.Base; + ti->sram_virt = ioremap(link->resource[3]->start, + resource_size(link->resource[3])); + ti->sram_phys = link->resource[3]->start; ret = pcmcia_request_configuration(link, &link->conf); if (ret) @@ -316,7 +314,7 @@ static void ibmtr_release(struct pcmcia_device *link) dev_dbg(&link->dev, "ibmtr_release\n"); - if (link->win) { + if (link->resource[2]->end) { struct tok_info *ti = netdev_priv(dev); iounmap(ti->mmio); } diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index f9b509a6b09a..aa6ee6b264cd 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -300,22 +300,22 @@ static void pcnet_detach(struct pcmcia_device *link) static hw_info_t *get_hwinfo(struct pcmcia_device *link) { struct net_device *dev = link->priv; - win_req_t req; u_char __iomem *base, *virt; int i, j; /* Allocate a small memory window */ - req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; - req.Base = 0; req.Size = 0; - req.AccessSpeed = 0; - i = pcmcia_request_window(link, &req, &link->win); + link->resource[2]->flags |= WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; + link->resource[2]->start = 0; link->resource[2]->end = 0; + i = pcmcia_request_window(link, link->resource[2], 0); if (i != 0) return NULL; - virt = ioremap(req.Base, req.Size); + virt = ioremap(link->resource[2]->start, + resource_size(link->resource[2])); for (i = 0; i < NR_INFO; i++) { - pcmcia_map_mem_page(link, link->win, hw_info[i].offset & ~(req.Size-1)); - base = &virt[hw_info[i].offset & (req.Size-1)]; + pcmcia_map_mem_page(link, link->resource[2], + hw_info[i].offset & ~(resource_size(link->resource[2])-1)); + base = &virt[hw_info[i].offset & (resource_size(link->resource[2])-1)]; if ((readb(base+0) == hw_info[i].a0) && (readb(base+2) == hw_info[i].a1) && (readb(base+4) == hw_info[i].a2)) { @@ -326,7 +326,7 @@ static hw_info_t *get_hwinfo(struct pcmcia_device *link) } iounmap(virt); - j = pcmcia_release_window(link, link->win); + j = pcmcia_release_window(link, link->resource[2]); return (i < NR_INFO) ? hw_info+i : NULL; } /* get_hwinfo */ @@ -1486,7 +1486,6 @@ static int setup_shmem_window(struct pcmcia_device *link, int start_pg, { struct net_device *dev = link->priv; pcnet_dev_t *info = PRIV(dev); - win_req_t req; int i, window_size, offset, ret; window_size = (stop_pg - start_pg) << 8; @@ -1497,22 +1496,22 @@ static int setup_shmem_window(struct pcmcia_device *link, int start_pg, window_size = roundup_pow_of_two(window_size); /* Allocate a memory window */ - req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE; - req.Attributes |= WIN_USE_WAIT; - req.Base = 0; req.Size = window_size; - req.AccessSpeed = mem_speed; - ret = pcmcia_request_window(link, &req, &link->win); + link->resource[3]->flags |= WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE; + link->resource[3]->flags |= WIN_USE_WAIT; + link->resource[3]->start = 0; link->resource[3]->end = window_size; + ret = pcmcia_request_window(link, link->resource[3], mem_speed); if (ret) goto failed; offset = (start_pg << 8) + cm_offset; offset -= offset % window_size; - ret = pcmcia_map_mem_page(link, link->win, offset); + ret = pcmcia_map_mem_page(link, link->resource[3], offset); if (ret) goto failed; /* Try scribbling on the buffer */ - info->base = ioremap(req.Base, window_size); + info->base = ioremap(link->resource[3]->start, + resource_size(link->resource[3])); for (i = 0; i < (TX_PAGES<<8); i += 2) __raw_writew((i>>1), info->base+offset+i); udelay(100); @@ -1521,19 +1520,20 @@ static int setup_shmem_window(struct pcmcia_device *link, int start_pg, pcnet_reset_8390(dev); if (i != (TX_PAGES<<8)) { iounmap(info->base); - pcmcia_release_window(link, link->win); - info->base = NULL; link->win = 0; + pcmcia_release_window(link, link->resource[3]); + info->base = NULL; goto failed; } ei_status.mem = info->base + offset; - ei_status.priv = req.Size; + ei_status.priv = resource_size(link->resource[3]); dev->mem_start = (u_long)ei_status.mem; - dev->mem_end = dev->mem_start + req.Size; + dev->mem_end = dev->mem_start + resource_size(link->resource[3]); ei_status.tx_start_page = start_pg; ei_status.rx_start_page = start_pg + TX_PAGES; - ei_status.stop_page = start_pg + ((req.Size - offset) >> 8); + ei_status.stop_page = start_pg + ( + (resource_size(link->resource[3]) - offset) >> 8); /* set up block i/o functions */ ei_status.get_8390_hdr = &shmem_get_8390_hdr; diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c index 377367d03b41..acc680739c89 100644 --- a/drivers/net/pcmcia/smc91c92_cs.c +++ b/drivers/net/pcmcia/smc91c92_cs.c @@ -442,7 +442,6 @@ static int mhz_mfc_config(struct pcmcia_device *link) { struct net_device *dev = link->priv; struct smc_private *smc = netdev_priv(dev); - win_req_t req; unsigned int offset; int i; @@ -459,16 +458,16 @@ static int mhz_mfc_config(struct pcmcia_device *link) dev->base_addr = link->resource[0]->start; /* Allocate a memory window, for accessing the ISR */ - req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; - req.Base = req.Size = 0; - req.AccessSpeed = 0; - i = pcmcia_request_window(link, &req, &link->win); + link->resource[2]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; + link->resource[2]->start = link->resource[2]->end = 0; + i = pcmcia_request_window(link, link->resource[2], 0); if (i != 0) return -ENODEV; - smc->base = ioremap(req.Base, req.Size); + smc->base = ioremap(link->resource[2]->start, + resource_size(link->resource[2])); offset = (smc->manfid == MANFID_MOTOROLA) ? link->conf.ConfigBase : 0; - i = pcmcia_map_mem_page(link, link->win, offset); + i = pcmcia_map_mem_page(link, link->resource[2], offset); if ((i == 0) && (smc->manfid == MANFID_MEGAHERTZ) && (smc->cardid == PRODID_MEGAHERTZ_EM3288)) @@ -999,7 +998,7 @@ config_failed: static void smc91c92_release(struct pcmcia_device *link) { dev_dbg(&link->dev, "smc91c92_release\n"); - if (link->win) { + if (link->resource[2]->end) { struct net_device *dev = link->priv; struct smc_private *smc = netdev_priv(dev); iounmap(smc->base); diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c index f5819526b5ee..4308bda0e96d 100644 --- a/drivers/net/pcmcia/xirc2ps_cs.c +++ b/drivers/net/pcmcia/xirc2ps_cs.c @@ -869,8 +869,6 @@ xirc2ps_config(struct pcmcia_device * link) goto config_error; if (local->dingo) { - win_req_t req; - /* Reset the modem's BAR to the correct value * This is necessary because in the RequestConfiguration call, * the base address of the ethernet port (BasePort1) is written @@ -890,14 +888,14 @@ xirc2ps_config(struct pcmcia_device * link) * is at 0x0800. So we allocate a window into the attribute * memory and write direct to the CIS registers */ - req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; - req.Base = req.Size = 0; - req.AccessSpeed = 0; - if ((err = pcmcia_request_window(link, &req, &link->win))) + link->resource[2]->flags = WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_AM | + WIN_ENABLE; + link->resource[2]->start = link->resource[2]->end = 0; + if ((err = pcmcia_request_window(link, link->resource[2], 0))) goto config_error; - local->dingo_ccr = ioremap(req.Base,0x1000) + 0x0800; - if ((err = pcmcia_map_mem_page(link, link->win, 0))) + local->dingo_ccr = ioremap(link->resource[2]->start, 0x1000) + 0x0800; + if ((err = pcmcia_map_mem_page(link, link->resource[2], 0))) goto config_error; /* Setup the CCRs; there are no infos in the CIS about the Ethernet @@ -988,7 +986,7 @@ xirc2ps_release(struct pcmcia_device *link) { dev_dbg(&link->dev, "release\n"); - if (link->win) { + if (link->resource[2]->end) { struct net_device *dev = link->priv; local_info_t *local = netdev_priv(dev); if (local->dingo) diff --git a/drivers/net/wireless/b43/pcmcia.c b/drivers/net/wireless/b43/pcmcia.c index dfbc41d431ff..618d9b5c1054 100644 --- a/drivers/net/wireless/b43/pcmcia.c +++ b/drivers/net/wireless/b43/pcmcia.c @@ -63,7 +63,6 @@ static int b43_pcmcia_resume(struct pcmcia_device *dev) static int __devinit b43_pcmcia_probe(struct pcmcia_device *dev) { struct ssb_bus *ssb; - win_req_t win; int err = -ENOMEM; int res = 0; @@ -76,16 +75,15 @@ static int __devinit b43_pcmcia_probe(struct pcmcia_device *dev) dev->conf.Attributes = CONF_ENABLE_IRQ; dev->conf.IntType = INT_MEMORY_AND_IO; - win.Attributes = WIN_ENABLE | WIN_DATA_WIDTH_16 | + dev->resource[2]->flags |= WIN_ENABLE | WIN_DATA_WIDTH_16 | WIN_USE_WAIT; - win.Base = 0; - win.Size = SSB_CORE_SIZE; - win.AccessSpeed = 250; - res = pcmcia_request_window(dev, &win, &dev->win); + dev->resource[2]->start = 0; + dev->resource[2]->end = SSB_CORE_SIZE; + res = pcmcia_request_window(dev, dev->resource[2], 250); if (res != 0) goto err_kfree_ssb; - res = pcmcia_map_mem_page(dev, dev->win, 0); + res = pcmcia_map_mem_page(dev, dev->resource[2], 0); if (res != 0) goto err_disable; @@ -96,7 +94,7 @@ static int __devinit b43_pcmcia_probe(struct pcmcia_device *dev) if (res != 0) goto err_disable; - err = ssb_bus_pcmciabus_register(ssb, dev, win.Base); + err = ssb_bus_pcmciabus_register(ssb, dev, dev->resource[2]->start); if (err) goto err_disable; dev->priv = ssb; diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 88560d0ae50a..ab34cb8c56c7 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -391,7 +391,6 @@ static int ray_config(struct pcmcia_device *link) { int ret = 0; int i; - win_req_t req; struct net_device *dev = (struct net_device *)link->priv; ray_dev_t *local = netdev_priv(dev); @@ -420,46 +419,45 @@ static int ray_config(struct pcmcia_device *link) goto failed; /*** Set up 32k window for shared memory (transmit and control) ************/ - req.Attributes = - WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE | WIN_USE_WAIT; - req.Base = 0; - req.Size = 0x8000; - req.AccessSpeed = ray_mem_speed; - ret = pcmcia_request_window(link, &req, &link->win); + link->resource[2]->flags |= WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE | WIN_USE_WAIT; + link->resource[2]->start = 0; + link->resource[2]->end = 0x8000; + ret = pcmcia_request_window(link, link->resource[2], ray_mem_speed); if (ret) goto failed; - ret = pcmcia_map_mem_page(link, link->win, 0); + ret = pcmcia_map_mem_page(link, link->resource[2], 0); if (ret) goto failed; - local->sram = ioremap(req.Base, req.Size); + local->sram = ioremap(link->resource[2]->start, + resource_size(link->resource[2])); /*** Set up 16k window for shared memory (receive buffer) ***************/ - req.Attributes = + link->resource[3]->flags |= WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE | WIN_USE_WAIT; - req.Base = 0; - req.Size = 0x4000; - req.AccessSpeed = ray_mem_speed; - ret = pcmcia_request_window(link, &req, &local->rmem_handle); + link->resource[3]->start = 0; + link->resource[3]->end = 0x4000; + ret = pcmcia_request_window(link, link->resource[3], ray_mem_speed); if (ret) goto failed; - ret = pcmcia_map_mem_page(link, local->rmem_handle, 0x8000); + ret = pcmcia_map_mem_page(link, link->resource[3], 0x8000); if (ret) goto failed; - local->rmem = ioremap(req.Base, req.Size); + local->rmem = ioremap(link->resource[3]->start, + resource_size(link->resource[3])); /*** Set up window for attribute memory ***********************************/ - req.Attributes = + link->resource[4]->flags |= WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_AM | WIN_ENABLE | WIN_USE_WAIT; - req.Base = 0; - req.Size = 0x1000; - req.AccessSpeed = ray_mem_speed; - ret = pcmcia_request_window(link, &req, &local->amem_handle); + link->resource[4]->start = 0; + link->resource[4]->end = 0x1000; + ret = pcmcia_request_window(link, link->resource[4], ray_mem_speed); if (ret) goto failed; - ret = pcmcia_map_mem_page(link, local->amem_handle, 0); + ret = pcmcia_map_mem_page(link, link->resource[4], 0); if (ret) goto failed; - local->amem = ioremap(req.Base, req.Size); + local->amem = ioremap(link->resource[4]->start, + resource_size(link->resource[4])); dev_dbg(&link->dev, "ray_config sram=%p\n", local->sram); dev_dbg(&link->dev, "ray_config rmem=%p\n", local->rmem); diff --git a/drivers/net/wireless/ray_cs.h b/drivers/net/wireless/ray_cs.h index 9f01ddb19748..e79848fbcca1 100644 --- a/drivers/net/wireless/ray_cs.h +++ b/drivers/net/wireless/ray_cs.h @@ -25,8 +25,6 @@ struct beacon_rx { typedef struct ray_dev_t { int card_status; int authentication_state; - window_handle_t amem_handle; /* handle to window for attribute memory */ - window_handle_t rmem_handle; /* handle to window for rx buffer on card */ void __iomem *sram; /* pointer to beginning of shared RAM */ void __iomem *amem; /* pointer to attribute mem window */ void __iomem *rmem; /* pointer to receive buffer window */ diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index 9ba4dade69a4..bf16a1cf7399 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -204,11 +204,10 @@ int pcmcia_write_config_byte(struct pcmcia_device *p_dev, off_t where, u8 val) EXPORT_SYMBOL(pcmcia_write_config_byte); -int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t wh, +int pcmcia_map_mem_page(struct pcmcia_device *p_dev, struct resource *res, unsigned int offset) { struct pcmcia_socket *s = p_dev->socket; - struct resource *res = wh; unsigned int w; int ret; @@ -386,7 +385,12 @@ out: return ret; } /* pcmcia_release_io */ - +/** + * pcmcia_release_window() - release reserved iomem for PCMCIA devices + * + * pcmcia_release_window() releases struct resource *res which was + * previously reserved by calling pcmcia_request_window(). + */ int pcmcia_release_window(struct pcmcia_device *p_dev, struct resource *res) { struct pcmcia_socket *s = p_dev->socket; @@ -420,6 +424,8 @@ int pcmcia_release_window(struct pcmcia_device *p_dev, struct resource *res) kfree(win->res); win->res = NULL; } + res->start = res->end = 0; + res->flags = IORESOURCE_MEM; p_dev->_win &= ~CLIENT_WIN_REQ(w); mutex_unlock(&s->ops_mutex); @@ -795,17 +801,21 @@ int pcmcia_setup_irq(struct pcmcia_device *p_dev) } -/** pcmcia_request_window +/** + * pcmcia_request_window() - attempt to reserve iomem for PCMCIA devices * - * Request_window() establishes a mapping between card memory space - * and system memory space. + * pcmcia_request_window() attepts to reserve an iomem ranges specified in + * struct resource *res pointing to one of the entries in + * struct pcmcia_device *p_dev->resource[2..5]. The "start" value is the + * requested start of the IO mem resource; "end" reflects the size + * requested. */ -int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_handle_t *wh) +int pcmcia_request_window(struct pcmcia_device *p_dev, struct resource *res, + unsigned int speed) { struct pcmcia_socket *s = p_dev->socket; pccard_mem_map *win; u_long align; - struct resource *res; int w; if (!(s->state & SOCKET_PRESENT)) { @@ -814,19 +824,19 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha } /* Window size defaults to smallest available */ - if (req->Size == 0) - req->Size = s->map_size; - align = (s->features & SS_CAP_MEM_ALIGN) ? req->Size : s->map_size; - if (req->Size & (s->map_size-1)) { + if (res->end == 0) + res->end = s->map_size; + align = (s->features & SS_CAP_MEM_ALIGN) ? res->end : s->map_size; + if (res->end & (s->map_size-1)) { dev_dbg(&p_dev->dev, "invalid map size\n"); return -EINVAL; } - if ((req->Base && (s->features & SS_CAP_STATIC_MAP)) || - (req->Base & (align-1))) { + if ((res->start && (s->features & SS_CAP_STATIC_MAP)) || + (res->start & (align-1))) { dev_dbg(&p_dev->dev, "invalid base address\n"); return -EINVAL; } - if (req->Base) + if (res->start) align = 0; /* Allocate system memory window */ @@ -843,7 +853,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha win = &s->win[w]; if (!(s->features & SS_CAP_STATIC_MAP)) { - win->res = pcmcia_find_mem_region(req->Base, req->Size, align, + win->res = pcmcia_find_mem_region(res->start, res->end, align, 0, s); if (!win->res) { dev_dbg(&p_dev->dev, "allocating mem region failed\n"); @@ -855,8 +865,8 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha /* Configure the socket controller */ win->map = w+1; - win->flags = req->Attributes; - win->speed = req->AccessSpeed; + win->flags = res->flags & WIN_FLAGS_MAP; + win->speed = speed; win->card_start = 0; if (s->ops->set_mem_map(s, win) != 0) { @@ -868,17 +878,14 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha /* Return window handle */ if (s->features & SS_CAP_STATIC_MAP) - req->Base = win->static_start; + res->start = win->static_start; else - req->Base = win->res->start; + res->start = win->res->start; /* convert to new-style resources */ - res = p_dev->resource[w + MAX_IO_WIN]; - res->start = req->Base; - res->end = req->Base + req->Size - 1; - res->flags &= ~IORESOURCE_BITS; - res->flags |= (req->Attributes & WIN_FLAGS_MAP) | (win->map << 2); - res->flags |= IORESOURCE_MEM; + res->end += res->start - 1; + res->flags &= ~WIN_FLAGS_REQ; + res->flags |= (win->map << 2) | IORESOURCE_MEM; res->parent = win->res; if (win->res) request_resource(&iomem_resource, res); @@ -886,7 +893,6 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha dev_dbg(&p_dev->dev, "request_window results in %pR\n", res); mutex_unlock(&s->ops_mutex); - *wh = res; return 0; } /* pcmcia_request_window */ diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c index dd9b40306f3d..e872e0684273 100644 --- a/drivers/scsi/pcmcia/nsp_cs.c +++ b/drivers/scsi/pcmcia/nsp_cs.c @@ -1596,18 +1596,13 @@ static void nsp_cs_detach(struct pcmcia_device *link) ethernet device available to the system. ======================================================================*/ -struct nsp_cs_configdata { - nsp_hw_data *data; - win_req_t req; -}; - static int nsp_cs_config_check(struct pcmcia_device *p_dev, cistpl_cftable_entry_t *cfg, cistpl_cftable_entry_t *dflt, unsigned int vcc, void *priv_data) { - struct nsp_cs_configdata *cfg_mem = priv_data; + nsp_hw_data *data = priv_data; if (cfg->index == 0) return -ENODEV; @@ -1663,21 +1658,24 @@ static int nsp_cs_config_check(struct pcmcia_device *p_dev, if ((cfg->mem.nwin > 0) || (dflt->mem.nwin > 0)) { cistpl_mem_t *mem = (cfg->mem.nwin) ? &cfg->mem : &dflt->mem; - cfg_mem->req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM; - cfg_mem->req.Attributes |= WIN_ENABLE; - cfg_mem->req.Base = mem->win[0].host_addr; - cfg_mem->req.Size = mem->win[0].len; - if (cfg_mem->req.Size < 0x1000) - cfg_mem->req.Size = 0x1000; - cfg_mem->req.AccessSpeed = 0; - if (pcmcia_request_window(p_dev, &cfg_mem->req, &p_dev->win) != 0) + p_dev->resource[2]->flags |= (WIN_DATA_WIDTH_16 | + WIN_MEMORY_TYPE_CM | + WIN_ENABLE); + p_dev->resource[2]->start = mem->win[0].host_addr; + p_dev->resource[2]->end = mem->win[0].len; + if (p_dev->resource[2]->end < 0x1000) + p_dev->resource[2]->end = 0x1000; + if (pcmcia_request_window(p_dev, p_dev->resource[2], + 0) != 0) goto next_entry; - if (pcmcia_map_mem_page(p_dev, p_dev->win, + if (pcmcia_map_mem_page(p_dev, p_dev->resource[2], mem->win[0].card_addr) != 0) goto next_entry; - cfg_mem->data->MmioAddress = (unsigned long) ioremap_nocache(cfg_mem->req.Base, cfg_mem->req.Size); - cfg_mem->data->MmioLength = cfg_mem->req.Size; + data->MmioAddress = (unsigned long) + ioremap_nocache(p_dev->resource[2]->start, + resource_size(p_dev->resource[2])); + data->MmioLength = resource_size(p_dev->resource[2]); } /* If we got this far, we're cool! */ return 0; @@ -1693,18 +1691,12 @@ static int nsp_cs_config(struct pcmcia_device *link) { int ret; scsi_info_t *info = link->priv; - struct nsp_cs_configdata *cfg_mem; struct Scsi_Host *host; nsp_hw_data *data = &nsp_data_base; nsp_dbg(NSP_DEBUG_INIT, "in"); - cfg_mem = kzalloc(sizeof(*cfg_mem), GFP_KERNEL); - if (!cfg_mem) - return -ENOMEM; - cfg_mem->data = data; - - ret = pcmcia_loop_config(link, nsp_cs_config_check, cfg_mem); + ret = pcmcia_loop_config(link, nsp_cs_config_check, data); if (ret) goto cs_failed; @@ -1767,18 +1759,15 @@ static int nsp_cs_config(struct pcmcia_device *link) printk(", io %pR", link->resource[0]); if (link->resource[1]) printk(" & %pR", link->resource[1]); - if (link->win) - printk(", mem 0x%06lx-0x%06lx", cfg_mem->req.Base, - cfg_mem->req.Base+cfg_mem->req.Size-1); + if (link->resource[2]) + printk(", mem %pR", link->resource[2]); printk("\n"); - kfree(cfg_mem); return 0; cs_failed: nsp_dbg(NSP_DEBUG_INIT, "config fail"); nsp_cs_release(link); - kfree(cfg_mem); return -ENODEV; } /* nsp_cs_config */ @@ -1807,7 +1796,7 @@ static void nsp_cs_release(struct pcmcia_device *link) scsi_remove_host(info->host); } - if (link->win) { + if (resource_size(link->resource[2])) { if (data != NULL) { iounmap((void *)(data->MmioAddress)); } diff --git a/include/pcmcia/cs.h b/include/pcmcia/cs.h index 68d8bde7e8d6..63cb9bbe390e 100644 --- a/include/pcmcia/cs.h +++ b/include/pcmcia/cs.h @@ -68,28 +68,4 @@ typedef struct config_req_t { #define PRESENT_IOBASE_3 0x100 #define PRESENT_IOSIZE 0x200 -/* For RequestWindow */ -typedef struct win_req_t { - u_int Attributes; - u_long Base; - u_int Size; - u_int AccessSpeed; -} win_req_t; - -/* Attributes for RequestWindow */ -#define WIN_MEMORY_TYPE_CM 0x00 /* default */ -#define WIN_MEMORY_TYPE_AM 0x20 /* MAP_ATTRIB */ -#define WIN_DATA_WIDTH_8 0x00 /* default */ -#define WIN_DATA_WIDTH_16 0x02 /* MAP_16BIT */ -#define WIN_ENABLE 0x01 /* MAP_ACTIVE */ -#define WIN_USE_WAIT 0x40 /* MAP_USE_WAIT */ - -#define WIN_FLAGS_MAP 0x63 /* MAP_ATTRIB | MAP_16BIT | MAP_ACTIVE | - MAP_USE_WAIT */ -#define WIN_FLAGS_REQ 0x1c /* mapping to socket->win[i]: - 0x04 -> 0 - 0x08 -> 1 - 0x0c -> 2 - 0x10 -> 3 */ - #endif /* _LINUX_CS_H */ diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h index 70c58ed2278c..6f7cb38d8850 100644 --- a/include/pcmcia/ds.h +++ b/include/pcmcia/ds.h @@ -36,8 +36,6 @@ struct pcmcia_device; struct config_t; struct net_device; -typedef struct resource *window_handle_t; - /* dynamic device IDs for PCMCIA device drivers. See * Documentation/pcmcia/driver.txt for details. */ @@ -92,7 +90,6 @@ struct pcmcia_device { /* deprecated, will be cleaned up soon */ config_req_t conf; - window_handle_t win; /* device setup */ unsigned int irq; @@ -209,10 +206,10 @@ int __must_check pcmcia_request_irq(struct pcmcia_device *p_dev, int pcmcia_request_configuration(struct pcmcia_device *p_dev, config_req_t *req); -int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, - window_handle_t *wh); -int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t win); -int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t win, +int pcmcia_request_window(struct pcmcia_device *p_dev, struct resource *res, + unsigned int speed); +int pcmcia_release_window(struct pcmcia_device *p_dev, struct resource *res); +int pcmcia_map_mem_page(struct pcmcia_device *p_dev, struct resource *res, unsigned int offset); int pcmcia_modify_configuration(struct pcmcia_device *p_dev, modconf_t *mod); @@ -234,6 +231,23 @@ static inline int pcmcia_io_cfg_data_width(unsigned int flags) return IO_DATA_PATH_WIDTH_AUTO; } +/* IO memory */ +#define WIN_MEMORY_TYPE_CM 0x00 /* default */ +#define WIN_MEMORY_TYPE_AM 0x20 /* MAP_ATTRIB */ +#define WIN_DATA_WIDTH_8 0x00 /* default */ +#define WIN_DATA_WIDTH_16 0x02 /* MAP_16BIT */ +#define WIN_ENABLE 0x01 /* MAP_ACTIVE */ +#define WIN_USE_WAIT 0x40 /* MAP_USE_WAIT */ + +#define WIN_FLAGS_MAP 0x63 /* MAP_ATTRIB | MAP_16BIT | MAP_ACTIVE | + MAP_USE_WAIT */ +#define WIN_FLAGS_REQ 0x1c /* mapping to socket->win[i]: + 0x04 -> 0 + 0x08 -> 1 + 0x0c -> 2 + 0x10 -> 3 */ + + #endif /* __KERNEL__ */ #endif /* _LINUX_DS_H */ -- cgit v1.2.3-70-g09d2 From 1ac71e5a35eebee60cdcf15b3980bd94498f037b Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Thu, 29 Jul 2010 19:27:09 +0200 Subject: pcmcia: convert pcmcia_request_configuration to pcmcia_enable_device pcmcia_enable_device() now replaces pcmcia_request_configuration(). Instead of config_req_t, all necessary flags are either passed as a parameter to pcmcia_enable_device(), or (in rare circumstances) set in struct pcmcia_device -> flags. With the last remaining user of include/pcmcia/cs.h gone, remove all references. CC: netdev@vger.kernel.org CC: linux-wireless@vger.kernel.org CC: linux-ide@vger.kernel.org CC: linux-usb@vger.kernel.org CC: laforge@gnumonks.org CC: linux-mtd@lists.infradead.org CC: alsa-devel@alsa-project.org CC: linux-serial@vger.kernel.org CC: Jiri Kosina CC: linux-scsi@vger.kernel.org Acked-by: Gustavo F. Padovan (for drivers/bluetooth) Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- Documentation/pcmcia/driver-changes.txt | 6 ++++ drivers/ata/pata_pcmcia.c | 5 ++-- drivers/bluetooth/bluecard_cs.c | 5 ++-- drivers/bluetooth/bt3c_cs.c | 5 ++-- drivers/bluetooth/btuart_cs.c | 5 ++-- drivers/bluetooth/dtl1_cs.c | 5 ++-- drivers/char/pcmcia/cm4000_cs.c | 3 +- drivers/char/pcmcia/cm4040_cs.c | 5 ++-- drivers/char/pcmcia/ipwireless/main.c | 6 ++-- drivers/char/pcmcia/ipwireless/main.h | 1 - drivers/char/pcmcia/ipwireless/tty.h | 1 - drivers/char/pcmcia/synclink_cs.c | 10 ++----- drivers/ide/ide-cs.c | 6 ++-- drivers/isdn/hardware/avm/avm_cs.c | 5 ++-- drivers/isdn/hisax/avma1_cs.c | 5 ++-- drivers/isdn/hisax/elsa_cs.c | 8 ++---- drivers/isdn/hisax/sedlbauer_cs.c | 12 +++----- drivers/isdn/hisax/teles_cs.c | 8 ++---- drivers/mmc/host/sdricoh_cs.c | 1 - drivers/mtd/maps/pcmciamtd.c | 6 +--- drivers/net/pcmcia/3c574_cs.c | 5 ++-- drivers/net/pcmcia/3c589_cs.c | 5 ++-- drivers/net/pcmcia/axnet_cs.c | 10 +++---- drivers/net/pcmcia/com20020_cs.c | 5 ++-- drivers/net/pcmcia/fmvj18x_cs.c | 5 ++-- drivers/net/pcmcia/ibmtr_cs.c | 5 ++-- drivers/net/pcmcia/nmclan_cs.c | 5 ++-- drivers/net/pcmcia/pcnet_cs.c | 7 ++--- drivers/net/pcmcia/smc91c92_cs.c | 9 +++--- drivers/net/pcmcia/xirc2ps_cs.c | 12 ++++---- drivers/net/wireless/airo_cs.c | 16 ++--------- drivers/net/wireless/atmel_cs.c | 16 ++--------- drivers/net/wireless/b43/pcmcia.c | 5 ++-- drivers/net/wireless/hostap/hostap_cs.c | 10 +++---- drivers/net/wireless/libertas/if_cs.c | 10 ++----- drivers/net/wireless/orinoco/orinoco_cs.c | 12 ++------ drivers/net/wireless/orinoco/spectrum_cs.c | 12 ++------ drivers/net/wireless/ray_cs.c | 5 ++-- drivers/net/wireless/wl3501_cs.c | 5 ++-- drivers/parport/parport_cs.c | 5 ++-- drivers/pcmcia/au1000_generic.h | 1 - drivers/pcmcia/au1000_pb1x00.c | 1 - drivers/pcmcia/cistpl.c | 1 - drivers/pcmcia/cs.c | 1 - drivers/pcmcia/cs_internal.h | 5 ---- drivers/pcmcia/ds.c | 3 +- drivers/pcmcia/i82092.c | 1 - drivers/pcmcia/i82365.c | 1 - drivers/pcmcia/m32r_cfc.c | 1 - drivers/pcmcia/m32r_pcc.c | 1 - drivers/pcmcia/m8xx_pcmcia.c | 1 - drivers/pcmcia/pcmcia_cis.c | 1 - drivers/pcmcia/pcmcia_resource.c | 33 +++++++++++----------- drivers/pcmcia/pd6729.c | 1 - drivers/pcmcia/rsrc_iodyn.c | 1 - drivers/pcmcia/rsrc_mgr.c | 1 - drivers/pcmcia/rsrc_nonstatic.c | 1 - drivers/pcmcia/sa1100_generic.c | 1 - drivers/pcmcia/soc_common.h | 1 - drivers/pcmcia/socket_sysfs.c | 1 - drivers/pcmcia/tcic.c | 1 - drivers/pcmcia/xxs1500_ss.c | 1 - drivers/pcmcia/yenta_socket.c | 1 - drivers/scsi/pcmcia/aha152x_stub.c | 5 ++-- drivers/scsi/pcmcia/fdomain_stub.c | 5 ++-- drivers/scsi/pcmcia/nsp_cs.c | 13 ++++----- drivers/scsi/pcmcia/qlogic_stub.c | 7 ++--- drivers/scsi/pcmcia/sym53c500_cs.c | 5 ++-- drivers/serial/serial_cs.c | 11 ++++---- drivers/ssb/main.c | 1 - drivers/ssb/pcmcia.c | 1 - drivers/ssb/scan.c | 1 - drivers/staging/comedi/drivers/cb_das16_cs.c | 11 ++------ drivers/staging/comedi/drivers/das08_cs.c | 17 ++---------- drivers/staging/comedi/drivers/ni_daq_700.c | 19 +++---------- drivers/staging/comedi/drivers/ni_daq_dio24.c | 19 +++---------- drivers/staging/comedi/drivers/ni_labpc_cs.c | 20 ++++---------- drivers/staging/comedi/drivers/ni_mio_cs.c | 5 ++-- drivers/staging/comedi/drivers/quatech_daqp_cs.c | 17 ++---------- drivers/staging/wlags49_h2/wl_cs.c | 7 ++--- drivers/staging/wlags49_h2/wl_internal.h | 1 - drivers/telephony/ixj_pcmcia.c | 3 +- drivers/usb/host/sl811_cs.c | 7 ++--- include/pcmcia/cs.h | 35 ------------------------ include/pcmcia/ds.h | 17 ++++++++---- include/pcmcia/ss.h | 1 - sound/pcmcia/pdaudiocf/pdaudiocf.c | 5 ++-- sound/pcmcia/pdaudiocf/pdaudiocf.h | 1 - sound/pcmcia/vx/vxpocket.c | 6 ++-- sound/pcmcia/vx/vxpocket.h | 1 - 90 files changed, 179 insertions(+), 395 deletions(-) delete mode 100644 include/pcmcia/cs.h (limited to 'Documentation') diff --git a/Documentation/pcmcia/driver-changes.txt b/Documentation/pcmcia/driver-changes.txt index 86e0f491f85e..62a029f24f46 100644 --- a/Documentation/pcmcia/driver-changes.txt +++ b/Documentation/pcmcia/driver-changes.txt @@ -1,4 +1,10 @@ This file details changes in 2.6 which affect PCMCIA card driver authors: +* pcmcia_request_configuration -> pcmcia_enable_device (as of 2.6.36) + pcmcia_request_configuration() got renamed to pcmcia_enable_device(), + as it mirrors pcmcia_disable_device(). Configuration settings are now + stored in struct pcmcia_device, e.g. in the fields config_flags, + config_index, config_base, vpp. + * pcmcia_request_window changes (as of 2.6.36) Instead of win_req_t, drivers are now requested to fill out struct pcmcia_device *p_dev->resource[2,3,4,5] for up to four ioport diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index c2679c01188b..76da55d27e08 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -34,7 +34,6 @@ #include #include -#include #include #include #include @@ -249,7 +248,7 @@ static int pcmcia_init_one(struct pcmcia_device *pdev) /* Set up attributes in order to probe card and get resources */ pdev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; pdev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - pdev->conf.Attributes = CONF_ENABLE_IRQ; + pdev->config_flags |= CONF_ENABLE_IRQ; /* See if we have a manufacturer identifier. Use it to set is_kme for vendor quirks */ @@ -275,7 +274,7 @@ static int pcmcia_init_one(struct pcmcia_device *pdev) if (!pdev->irq) goto failed; - ret = pcmcia_request_configuration(pdev, &pdev->conf); + ret = pcmcia_enable_device(pdev); if (ret) goto failed; diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 08f4818ad9f7..c9dd5b789d25 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -39,7 +39,6 @@ #include #include -#include #include #include #include @@ -865,7 +864,7 @@ static int bluecard_probe(struct pcmcia_device *link) info->p_dev = link; link->priv = info; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; return bluecard_config(link); } @@ -905,7 +904,7 @@ static int bluecard_config(struct pcmcia_device *link) if (i != 0) goto failed; - i = pcmcia_request_configuration(link, &link->conf); + i = pcmcia_enable_device(link); if (i != 0) goto failed; diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index fb018073a5f8..3db95887cfd7 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -45,7 +45,6 @@ #include #include -#include #include #include #include @@ -660,7 +659,7 @@ static int bt3c_probe(struct pcmcia_device *link) link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; link->resource[0]->end = 8; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; return bt3c_config(link); } @@ -741,7 +740,7 @@ found_port: if (i != 0) goto failed; - i = pcmcia_request_configuration(link, &link->conf); + i = pcmcia_enable_device(link); if (i != 0) goto failed; diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 897c7c74ca14..c5c43594ae0e 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -41,7 +41,6 @@ #include #include -#include #include #include #include @@ -589,7 +588,7 @@ static int btuart_probe(struct pcmcia_device *link) link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; link->resource[0]->end = 8; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; return btuart_config(link); } @@ -670,7 +669,7 @@ found_port: if (i != 0) goto failed; - i = pcmcia_request_configuration(link, &link->conf); + i = pcmcia_enable_device(link); if (i != 0) goto failed; diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index b4c9a2e0a96b..38206df7206b 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -41,7 +41,6 @@ #include #include -#include #include #include #include @@ -575,7 +574,7 @@ static int dtl1_probe(struct pcmcia_device *link) link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; link->resource[0]->end = 8; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; return dtl1_config(link); } @@ -619,7 +618,7 @@ static int dtl1_config(struct pcmcia_device *link) if (i != 0) goto failed; - i = pcmcia_request_configuration(link, &link->conf); + i = pcmcia_enable_device(link); if (i != 0) goto failed; diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index d2accd64b3f5..75caa8c1b484 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -34,7 +34,6 @@ #include #include -#include #include #include #include @@ -1767,7 +1766,7 @@ static int cm4000_config(struct pcmcia_device * link, int devno) if (pcmcia_loop_config(link, cm4000_config_check, NULL)) goto cs_release; - if (pcmcia_request_configuration(link, &link->conf)) + if (pcmcia_enable_device(link)) goto cs_release; dev = link->priv; diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index a32eba0afdfc..0c87b80bf641 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -29,7 +29,6 @@ #include #include -#include #include #include #include @@ -547,10 +546,10 @@ static int reader_config(struct pcmcia_device *link, int devno) if (pcmcia_loop_config(link, cm4040_config_check, NULL)) goto cs_release; - fail_rc = pcmcia_request_configuration(link, &link->conf); + fail_rc = pcmcia_enable_device(link); if (fail_rc != 0) { dev_printk(KERN_INFO, &link->dev, - "pcmcia_request_configuration failed 0x%x\n", + "pcmcia_enable_device failed 0x%x\n", fail_rc); goto cs_release; } diff --git a/drivers/char/pcmcia/ipwireless/main.c b/drivers/char/pcmcia/ipwireless/main.c index 05c4e6834a6b..cd21b2bcdcd7 100644 --- a/drivers/char/pcmcia/ipwireless/main.c +++ b/drivers/char/pcmcia/ipwireless/main.c @@ -32,7 +32,6 @@ #include #include #include -#include static struct pcmcia_device_id ipw_ids[] = { PCMCIA_DEVICE_MANF_CARD(0x02f2, 0x0100), @@ -172,7 +171,7 @@ static int config_ipwireless(struct ipw_dev *ipw) if (ret != 0) return ret; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; INIT_WORK(&ipw->work_reboot, signalled_reboot_work); @@ -210,8 +209,7 @@ static int config_ipwireless(struct ipw_dev *ipw) * Do the RequestConfiguration last, because it enables interrupts. * Then we don't get any interrupts before we're ready for them. */ - ret = pcmcia_request_configuration(link, &link->conf); - + ret = pcmcia_enable_device(link); if (ret != 0) goto exit; diff --git a/drivers/char/pcmcia/ipwireless/main.h b/drivers/char/pcmcia/ipwireless/main.h index 90402195855e..f2cbb116bccb 100644 --- a/drivers/char/pcmcia/ipwireless/main.h +++ b/drivers/char/pcmcia/ipwireless/main.h @@ -21,7 +21,6 @@ #include #include -#include #include #include diff --git a/drivers/char/pcmcia/ipwireless/tty.h b/drivers/char/pcmcia/ipwireless/tty.h index 3e163d4cab15..747b2d637860 100644 --- a/drivers/char/pcmcia/ipwireless/tty.h +++ b/drivers/char/pcmcia/ipwireless/tty.h @@ -21,7 +21,6 @@ #include #include -#include #include #include diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 535aa0899e9f..99feaedc53a1 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -70,7 +70,6 @@ #include #include -#include #include #include #include @@ -550,8 +549,6 @@ static int mgslpc_probe(struct pcmcia_device *link) /* Initialize the struct pcmcia_device structure */ - link->conf.Attributes = 0; - ret = mgslpc_config(link); if (ret) return ret; @@ -593,14 +590,14 @@ static int mgslpc_config(struct pcmcia_device *link) if (ret != 0) goto failed; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; link->config_index = 8; link->config_regs = PRESENT_OPTION; ret = pcmcia_request_irq(link, mgslpc_isr); if (ret) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; @@ -609,8 +606,7 @@ static int mgslpc_config(struct pcmcia_device *link) dev_info(&link->dev, "index 0x%02x:", link->config_index); - if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq); + printk(", irq %d", link->irq); if (link->resource[0]) printk(", io %pR", link->resource[0]); printk("\n"); diff --git a/drivers/ide/ide-cs.c b/drivers/ide/ide-cs.c index 07e37876559c..87ad04925a9f 100644 --- a/drivers/ide/ide-cs.c +++ b/drivers/ide/ide-cs.c @@ -43,7 +43,6 @@ #include #include -#include #include #include #include @@ -99,7 +98,7 @@ static int ide_probe(struct pcmcia_device *link) link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; return ide_config(link); } /* ide_attach */ @@ -284,7 +283,8 @@ static int ide_config(struct pcmcia_device *link) if (!link->irq) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c index 2d8bbbf286aa..6ea5cd28d349 100644 --- a/drivers/isdn/hardware/avm/avm_cs.c +++ b/drivers/isdn/hardware/avm/avm_cs.c @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -79,7 +78,7 @@ static int avmcs_probe(struct pcmcia_device *p_dev) p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; /* General socket configuration */ - p_dev->conf.Attributes = CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; p_dev->config_index = 1; p_dev->config_regs = PRESENT_OPTION; @@ -149,7 +148,7 @@ static int avmcs_config(struct pcmcia_device *link) /* * configure the PCMCIA socket */ - i = pcmcia_request_configuration(link, &link->conf); + i = pcmcia_enable_device(link); if (i != 0) { pcmcia_disable_device(link); break; diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c index e25f6c7376ed..5dd47ad6ecc9 100644 --- a/drivers/isdn/hisax/avma1_cs.c +++ b/drivers/isdn/hisax/avma1_cs.c @@ -20,7 +20,6 @@ #include #include -#include #include #include #include "hisax_cfg.h" @@ -84,7 +83,7 @@ static int __devinit avma1cs_probe(struct pcmcia_device *p_dev) p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_16; /* General socket configuration */ - p_dev->conf.Attributes = CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; p_dev->config_index = 1; p_dev->config_regs = PRESENT_OPTION; @@ -160,7 +159,7 @@ static int __devinit avma1cs_config(struct pcmcia_device *link) /* * configure the PCMCIA socket */ - i = pcmcia_request_configuration(link, &link->conf); + i = pcmcia_enable_device(link); if (i != 0) { pcmcia_disable_device(link); break; diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c index f276e8428960..368c8a213f0b 100644 --- a/drivers/isdn/hisax/elsa_cs.c +++ b/drivers/isdn/hisax/elsa_cs.c @@ -46,7 +46,6 @@ #include #include -#include #include #include #include @@ -129,8 +128,6 @@ static int __devinit elsa_cs_probe(struct pcmcia_device *link) link->resource[0]->end = 8; link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->conf.Attributes = CONF_ENABLE_IRQ; - return elsa_cs_config(link); } /* elsa_cs_attach */ @@ -205,15 +202,14 @@ static int __devinit elsa_cs_config(struct pcmcia_device *link) if (!link->irq) goto failed; - i = pcmcia_request_configuration(link, &link->conf); + i = pcmcia_enable_device(link); if (i != 0) goto failed; /* Finally, report what we've done */ dev_info(&link->dev, "index 0x%02x: ", link->config_index); - if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq); + printk(", irq %d", link->irq); if (link->resource[0]) printk(" & %pR", link->resource[0]); if (link->resource[1]) diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c index 43d0a4e97ead..791e23a75f78 100644 --- a/drivers/isdn/hisax/sedlbauer_cs.c +++ b/drivers/isdn/hisax/sedlbauer_cs.c @@ -46,7 +46,6 @@ #include #include -#include #include #include #include @@ -132,8 +131,6 @@ static int __devinit sedlbauer_probe(struct pcmcia_device *link) link->resource[0]->end = 8; link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - link->conf.Attributes = 0; - return sedlbauer_config(link); } /* sedlbauer_attach */ @@ -175,7 +172,7 @@ static int sedlbauer_config_check(struct pcmcia_device *p_dev, /* Does this card need audio output? */ if (cfg->flags & CISTPL_CFTABLE_AUDIO) - p_dev->conf.Attributes |= CONF_ENABLE_SPKR; + p_dev->config_flags |= CONF_ENABLE_SPKR; /* Use power settings for Vcc and Vpp if present */ /* Note that the CIS values need to be rescaled */ @@ -192,7 +189,7 @@ static int sedlbauer_config_check(struct pcmcia_device *p_dev, else if (dflt->vpp1.present & (1<vpp = dflt->vpp1.param[CISTPL_POWER_VNOM]/10000; - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->resource[0]->end = p_dev->resource[1]->end = 0; @@ -247,7 +244,7 @@ static int __devinit sedlbauer_config(struct pcmcia_device *link) the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. */ - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; @@ -256,8 +253,7 @@ static int __devinit sedlbauer_config(struct pcmcia_device *link) link->config_index); if (link->vpp) printk(", Vpp %d.%d", link->vpp/10, link->vpp%10); - if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq); + printk(", irq %d", link->irq); if (link->resource[0]) printk(" & %pR", link->resource[0]); if (link->resource[1]) diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c index 614afc64b5b1..2ae71e3297ba 100644 --- a/drivers/isdn/hisax/teles_cs.c +++ b/drivers/isdn/hisax/teles_cs.c @@ -27,7 +27,6 @@ #include #include -#include #include #include #include @@ -109,7 +108,7 @@ static int __devinit teles_probe(struct pcmcia_device *link) link->resource[0]->end = 96; link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; return teles_cs_config(link); } /* teles_attach */ @@ -185,15 +184,14 @@ static int __devinit teles_cs_config(struct pcmcia_device *link) if (!link->irq) goto cs_failed; - i = pcmcia_request_configuration(link, &link->conf); + i = pcmcia_enable_device(link); if (i != 0) goto cs_failed; /* Finally, report what we've done */ dev_info(&link->dev, "index 0x%02x:", link->config_index); - if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq); + printk(", irq %d", link->irq); if (link->resource[0]) printk(" & %pR", link->resource[0]); if (link->resource[1]) diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c index 7aa65bb2af4a..7a7273b09d9a 100644 --- a/drivers/mmc/host/sdricoh_cs.c +++ b/drivers/mmc/host/sdricoh_cs.c @@ -30,7 +30,6 @@ #include #include -#include #include #include #include diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 99c7257363d6..388db9ecf222 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -568,7 +567,6 @@ static int pcmciamtd_config(struct pcmcia_device *link) dev->pcmcia_map.map_priv_2 = (unsigned long)link->resource[2]; dev->vpp = (vpp) ? vpp : link->socket->socket.Vpp; - link->conf.Attributes = 0; if(setvpp == 2) { link->vpp = dev->vpp; } else { @@ -577,7 +575,7 @@ static int pcmciamtd_config(struct pcmcia_device *link) link->config_index = 0; DEBUG(2, "Setting Configuration"); - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret != 0) { if (dev->win_base) { iounmap(dev->win_base); @@ -718,8 +716,6 @@ static int pcmciamtd_probe(struct pcmcia_device *link) dev->p_dev = link; link->priv = dev; - link->conf.Attributes = 0; - return pcmciamtd_config(link); } diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c index 4b670b3da378..8abce76367f8 100644 --- a/drivers/net/pcmcia/3c574_cs.c +++ b/drivers/net/pcmcia/3c574_cs.c @@ -87,7 +87,6 @@ earlier 3Com products. #include #include -#include #include #include #include @@ -280,7 +279,7 @@ static int tc574_probe(struct pcmcia_device *link) spin_lock_init(&lp->window_lock); link->resource[0]->end = 32; link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; link->config_index = 1; dev->netdev_ops = &el3_netdev_ops; @@ -351,7 +350,7 @@ static int tc574_config(struct pcmcia_device *link) if (ret) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c index 6549e2c496a0..34195c407fb2 100644 --- a/drivers/net/pcmcia/3c589_cs.c +++ b/drivers/net/pcmcia/3c589_cs.c @@ -41,7 +41,6 @@ #include #include -#include #include #include #include @@ -216,7 +215,7 @@ static int tc589_probe(struct pcmcia_device *link) link->resource[0]->end = 16; link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; link->config_index = 1; dev->netdev_ops = &el3_netdev_ops; @@ -293,7 +292,7 @@ static int tc589_config(struct pcmcia_device *link) if (ret) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c index 2c273ce6a5d8..f361d2865e34 100644 --- a/drivers/net/pcmcia/axnet_cs.c +++ b/drivers/net/pcmcia/axnet_cs.c @@ -39,7 +39,6 @@ #include #include "../8390.h" -#include #include #include #include @@ -166,7 +165,7 @@ static int axnet_probe(struct pcmcia_device *link) info = PRIV(dev); info->p_dev = link; link->priv = dev; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; dev->netdev_ops = &axnet_netdev_ops; @@ -332,11 +331,12 @@ static int axnet_config(struct pcmcia_device *link) if (!link->irq) goto failed; - + + link->config_flags |= CONF_ENABLE_IRQ; if (resource_size(link->resource[1]) == 8) - link->conf.Attributes |= CONF_ENABLE_SPKR; + link->config_flags |= CONF_ENABLE_SPKR; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/net/pcmcia/com20020_cs.c b/drivers/net/pcmcia/com20020_cs.c index a58eafed42a5..039731bddc27 100644 --- a/drivers/net/pcmcia/com20020_cs.c +++ b/drivers/net/pcmcia/com20020_cs.c @@ -43,7 +43,6 @@ #include #include -#include #include #include @@ -160,7 +159,7 @@ static int com20020_probe(struct pcmcia_device *p_dev) p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; p_dev->resource[0]->end = 16; - p_dev->conf.Attributes = CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; info->dev = dev; p_dev->priv = info; @@ -281,7 +280,7 @@ static int com20020_config(struct pcmcia_device *link) dev->irq = link->irq; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c index 23f5333d8029..f6865adb126d 100644 --- a/drivers/net/pcmcia/fmvj18x_cs.c +++ b/drivers/net/pcmcia/fmvj18x_cs.c @@ -49,7 +49,6 @@ #include #include -#include #include #include #include @@ -252,7 +251,7 @@ static int fmvj18x_probe(struct pcmcia_device *link) link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; /* General socket configuration */ - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; dev->netdev_ops = &fjn_netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; @@ -431,7 +430,7 @@ static int fmvj18x_config(struct pcmcia_device *link) ret = pcmcia_request_irq(link, fjn_interrupt); if (ret) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/pcmcia/ibmtr_cs.c index feedeeb17a52..b298a3d98dc8 100644 --- a/drivers/net/pcmcia/ibmtr_cs.c +++ b/drivers/net/pcmcia/ibmtr_cs.c @@ -57,7 +57,6 @@ #include #include -#include #include #include @@ -152,7 +151,7 @@ static int __devinit ibmtr_attach(struct pcmcia_device *link) link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; link->resource[0]->end = 4; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; link->config_regs = PRESENT_OPTION; info->dev = dev; @@ -269,7 +268,7 @@ static int __devinit ibmtr_config(struct pcmcia_device *link) resource_size(link->resource[3])); ti->sram_phys = link->resource[3]->start; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c index 98c4a6976045..51bf76de6499 100644 --- a/drivers/net/pcmcia/nmclan_cs.c +++ b/drivers/net/pcmcia/nmclan_cs.c @@ -146,7 +146,6 @@ Include Files #include #include -#include #include #include #include @@ -460,7 +459,7 @@ static int nmclan_probe(struct pcmcia_device *link) spin_lock_init(&lp->bank_lock); link->resource[0]->end = 32; link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; link->config_index = 1; link->config_regs = PRESENT_OPTION; @@ -649,7 +648,7 @@ static int nmclan_config(struct pcmcia_device *link) ret = pcmcia_request_exclusive_irq(link, mace_interrupt); if (ret) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index 68c46751f84f..4a3b6a43550f 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -42,7 +42,6 @@ #include #include "../8390.h" -#include #include #include #include @@ -260,7 +259,7 @@ static int pcnet_probe(struct pcmcia_device *link) info->p_dev = link; link->priv = dev; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; dev->netdev_ops = &pcnet_netdev_ops; @@ -560,13 +559,13 @@ static hw_info_t *pcnet_try_config(struct pcmcia_device *link, return NULL; if (resource_size(link->resource[1]) == 8) - link->conf.Attributes |= CONF_ENABLE_SPKR; + link->config_flags |= CONF_ENABLE_SPKR; if ((link->manf_id == MANFID_IBM) && (link->card_id == PRODID_IBM_HOME_AND_AWAY)) link->config_index |= 0x10; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) return NULL; diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c index 59f5034e8d93..8c16ba672012 100644 --- a/drivers/net/pcmcia/smc91c92_cs.c +++ b/drivers/net/pcmcia/smc91c92_cs.c @@ -44,7 +44,6 @@ #include #include -#include #include #include #include @@ -326,7 +325,7 @@ static int smc91c92_probe(struct pcmcia_device *link) spin_lock_init(&smc->lock); link->resource[0]->end = 16; link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; /* The SMC91c92-specific entries in the device structure. */ dev->netdev_ops = &smc_netdev_ops; @@ -444,7 +443,7 @@ static int mhz_mfc_config(struct pcmcia_device *link) unsigned int offset; int i; - link->conf.Attributes |= CONF_ENABLE_SPKR; + link->config_flags |= CONF_ENABLE_SPKR; link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; link->resource[1]->end = 8; @@ -637,7 +636,7 @@ static int osi_config(struct pcmcia_device *link) static const unsigned int com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; int i, j; - link->conf.Attributes |= CONF_ENABLE_SPKR; + link->config_flags |= CONF_ENABLE_SPKR; link->resource[0]->end = 64; link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; link->resource[1]->end = 8; @@ -862,7 +861,7 @@ static int smc91c92_config(struct pcmcia_device *link) i = pcmcia_request_irq(link, smc_interrupt); if (i) goto config_failed; - i = pcmcia_request_configuration(link, &link->conf); + i = pcmcia_enable_device(link); if (i) goto config_failed; diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c index 1c8ebf2df0c0..7a4a99b73d17 100644 --- a/drivers/net/pcmcia/xirc2ps_cs.c +++ b/drivers/net/pcmcia/xirc2ps_cs.c @@ -82,7 +82,6 @@ #include #include -#include #include #include #include @@ -529,7 +528,7 @@ xirc2ps_probe(struct pcmcia_device *link) link->priv = dev; /* General socket configuration */ - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; link->config_index = 1; /* Fill in card specific entries */ @@ -811,9 +810,6 @@ xirc2ps_config(struct pcmcia_device * link) if (local->modem) { int pass; - if (do_sound) - link->conf.Attributes |= CONF_ENABLE_SPKR; - link->resource[1]->end = 8; link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; if (local->dingo) { @@ -863,7 +859,11 @@ xirc2ps_config(struct pcmcia_device * link) * This actually configures the PCMCIA socket -- setting up * the I/O windows and the interrupt mapping. */ - if ((err=pcmcia_request_configuration(link, &link->conf))) + link->config_flags |= CONF_ENABLE_IRQ; + if (do_sound) + link->config_flags |= CONF_ENABLE_SPKR; + + if ((err = pcmcia_enable_device(link))) goto config_error; if (local->dingo) { diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/airo_cs.c index 40f9ed760bb3..5939d0c7a5c8 100644 --- a/drivers/net/wireless/airo_cs.c +++ b/drivers/net/wireless/airo_cs.c @@ -32,7 +32,6 @@ #include #include -#include #include #include #include @@ -96,15 +95,6 @@ static int airo_probe(struct pcmcia_device *p_dev) dev_dbg(&p_dev->dev, "airo_attach()\n"); - /* - General socket configuration defaults can go here. In this - client, we assume very little, and rely on the CIS for almost - everything. In most clients, many details (i.e., number, sizes, - and attributes of IO windows) are fixed by the nature of the - device, and can be hard-wired here. - */ - p_dev->conf.Attributes = 0; - /* Allocate space for private device-specific data */ local = kzalloc(sizeof(local_info_t), GFP_KERNEL); if (!local) { @@ -158,7 +148,7 @@ static int airo_cs_config_check(struct pcmcia_device *p_dev, /* Does this card need audio output? */ if (cfg->flags & CISTPL_CFTABLE_AUDIO) - p_dev->conf.Attributes |= CONF_ENABLE_SPKR; + p_dev->config_flags |= CONF_ENABLE_SPKR; /* Use power settings for Vcc and Vpp if present */ /* Note that the CIS values need to be rescaled */ @@ -167,7 +157,7 @@ static int airo_cs_config_check(struct pcmcia_device *p_dev, else if (dflt->vpp1.present & (1<vpp = dflt->vpp1.param[CISTPL_POWER_VNOM]/10000; - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->resource[0]->end = p_dev->resource[1]->end = 0; @@ -229,7 +219,7 @@ static int airo_config(struct pcmcia_device *link) the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. */ - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; ((local_info_t *)link->priv)->eth_dev = diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c index 8b75158caed7..080266eba985 100644 --- a/drivers/net/wireless/atmel_cs.c +++ b/drivers/net/wireless/atmel_cs.c @@ -42,7 +42,6 @@ #include #include -#include #include #include #include @@ -106,15 +105,6 @@ static int atmel_probe(struct pcmcia_device *p_dev) dev_dbg(&p_dev->dev, "atmel_attach()\n"); - /* - General socket configuration defaults can go here. In this - client, we assume very little, and rely on the CIS for almost - everything. In most clients, many details (i.e., number, sizes, - and attributes of IO windows) are fixed by the nature of the - device, and can be hard-wired here. - */ - p_dev->conf.Attributes = 0; - /* Allocate space for private device-specific data */ local = kzalloc(sizeof(local_info_t), GFP_KERNEL); if (!local) { @@ -175,7 +165,7 @@ static int atmel_config_check(struct pcmcia_device *p_dev, /* Does this card need audio output? */ if (cfg->flags & CISTPL_CFTABLE_AUDIO) - p_dev->conf.Attributes |= CONF_ENABLE_SPKR; + p_dev->config_flags |= CONF_ENABLE_SPKR; /* Use power settings for Vcc and Vpp if present */ /* Note that the CIS values need to be rescaled */ @@ -184,7 +174,7 @@ static int atmel_config_check(struct pcmcia_device *p_dev, else if (dflt->vpp1.present & (1<vpp = dflt->vpp1.param[CISTPL_POWER_VNOM]/10000; - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->resource[0]->end = p_dev->resource[1]->end = 0; @@ -242,7 +232,7 @@ static int atmel_config(struct pcmcia_device *link) the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. */ - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/net/wireless/b43/pcmcia.c b/drivers/net/wireless/b43/pcmcia.c index 138b26fcc75c..61abab1f1c7c 100644 --- a/drivers/net/wireless/b43/pcmcia.c +++ b/drivers/net/wireless/b43/pcmcia.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -72,7 +71,7 @@ static int __devinit b43_pcmcia_probe(struct pcmcia_device *dev) err = -ENODEV; - dev->conf.Attributes = CONF_ENABLE_IRQ; + dev->config_flags |= CONF_ENABLE_IRQ; dev->resource[2]->flags |= WIN_ENABLE | WIN_DATA_WIDTH_16 | WIN_USE_WAIT; @@ -89,7 +88,7 @@ static int __devinit b43_pcmcia_probe(struct pcmcia_device *dev) if (!dev->irq) goto err_disable; - res = pcmcia_request_configuration(dev, &dev->conf); + res = pcmcia_enable_device(dev); if (res != 0) goto err_disable; diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index 5704d3f9e1b7..5b0b5828b3cf 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -484,7 +483,7 @@ static int prism2_config_check(struct pcmcia_device *p_dev, /* Does this card need audio output? */ if (cfg->flags & CISTPL_CFTABLE_AUDIO) - p_dev->conf.Attributes |= CONF_ENABLE_SPKR; + p_dev->config_flags |= CONF_ENABLE_SPKR; /* Use power settings for Vcc and Vpp if present */ /* Note that the CIS values need to be rescaled */ @@ -510,7 +509,7 @@ static int prism2_config_check(struct pcmcia_device *p_dev, p_dev->vpp = dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000; /* Do we need to allocate an interrupt? */ - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; /* IO window settings */ PDEBUG(DEBUG_EXTRA, "IO window settings: cfg->io.nwin=%d " @@ -590,7 +589,7 @@ static int prism2_config(struct pcmcia_device *link) * the I/O windows and the interrupt mapping, and putting the * card and host interface into "Memory and IO" mode. */ - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed_unlock; @@ -605,8 +604,7 @@ static int prism2_config(struct pcmcia_device *link) if (link->vpp) printk(", Vpp %d.%d", link->vpp / 10, link->vpp % 10); - if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq); + printk(", irq %d", link->irq); if (link->resource[0]) printk(" & %pR", link->resource[0]); if (link->resource[1]) diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c index 814b7faaa365..6020c19b1bdb 100644 --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c @@ -28,7 +28,6 @@ #include #include -#include #include #include @@ -806,7 +805,7 @@ static int if_cs_ioprobe(struct pcmcia_device *p_dev, p_dev->resource[0]->end = cfg->io.win[0].len; /* Do we need to allocate an interrupt? */ - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; /* IO window settings */ if (cfg->io.nwin != 1) { @@ -835,14 +834,11 @@ static int if_cs_probe(struct pcmcia_device *p_dev) card->p_dev = p_dev; p_dev->priv = card; - p_dev->conf.Attributes = 0; - if (pcmcia_loop_config(p_dev, if_cs_ioprobe, NULL)) { lbs_pr_err("error in pcmcia_loop_config\n"); goto out1; } - /* * Allocate an interrupt line. Note that this does not assign * a handler to the interrupt, unless the 'Handler' member of @@ -865,9 +861,9 @@ static int if_cs_probe(struct pcmcia_device *p_dev) * the I/O windows and the interrupt mapping, and putting the * card and host interface into "Memory and IO" mode. */ - ret = pcmcia_request_configuration(p_dev, &p_dev->conf); + ret = pcmcia_enable_device(p_dev); if (ret) { - lbs_pr_err("error in pcmcia_request_configuration\n"); + lbs_pr_err("error in pcmcia_enable_device\n"); goto out2; } diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c index 1147d6bd4733..00316a1a1092 100644 --- a/drivers/net/wireless/orinoco/orinoco_cs.c +++ b/drivers/net/wireless/orinoco/orinoco_cs.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -117,13 +116,6 @@ orinoco_cs_probe(struct pcmcia_device *link) card->p_dev = link; link->priv = priv; - /* General socket configuration defaults can go here. In this - * client, we assume very little, and rely on the CIS for - * almost everything. In most clients, many details (i.e., - * number, sizes, and attributes of IO windows) are fixed by - * the nature of the device, and can be hard-wired here. */ - link->conf.Attributes = 0; - return orinoco_cs_config(link); } /* orinoco_cs_attach */ @@ -187,7 +179,7 @@ static int orinoco_cs_config_check(struct pcmcia_device *p_dev, dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000; /* Do we need to allocate an interrupt? */ - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->resource[0]->end = p_dev->resource[1]->end = 0; @@ -266,7 +258,7 @@ orinoco_cs_config(struct pcmcia_device *link) * the I/O windows and the interrupt mapping, and putting the * card and host interface into "Memory and IO" mode. */ - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c index 20b08ab87655..ca2c6c0c5576 100644 --- a/drivers/net/wireless/orinoco/spectrum_cs.c +++ b/drivers/net/wireless/orinoco/spectrum_cs.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -179,13 +178,6 @@ spectrum_cs_probe(struct pcmcia_device *link) card->p_dev = link; link->priv = priv; - /* General socket configuration defaults can go here. In this - * client, we assume very little, and rely on the CIS for - * almost everything. In most clients, many details (i.e., - * number, sizes, and attributes of IO windows) are fixed by - * the nature of the device, and can be hard-wired here. */ - link->conf.Attributes = 0; - return spectrum_cs_config(link); } /* spectrum_cs_attach */ @@ -249,7 +241,7 @@ static int spectrum_cs_config_check(struct pcmcia_device *p_dev, dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000; /* Do we need to allocate an interrupt? */ - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->resource[0]->end = p_dev->resource[1]->end = 0; @@ -329,7 +321,7 @@ spectrum_cs_config(struct pcmcia_device *link) * the I/O windows and the interrupt mapping, and putting the * card and host interface into "Memory and IO" mode. */ - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 7fb66cc1e9fd..1457f34efa9a 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -46,7 +46,6 @@ #include #include -#include #include #include #include @@ -318,7 +317,7 @@ static int ray_probe(struct pcmcia_device *p_dev) p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; /* General socket configuration */ - p_dev->conf.Attributes = CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; p_dev->config_index = 1; p_dev->priv = dev; @@ -413,7 +412,7 @@ static int ray_config(struct pcmcia_device *link) /* This actually configures the PCMCIA socket -- setting up the I/O windows and the interrupt mapping. */ - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 3947cf8e63c5..101b6ffd560e 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -48,7 +48,6 @@ #include -#include #include #include #include @@ -1888,7 +1887,7 @@ static int wl3501_probe(struct pcmcia_device *p_dev) p_dev->resource[0]->flags = IO_DATA_PATH_WIDTH_8; /* General socket configuration */ - p_dev->conf.Attributes = CONF_ENABLE_IRQ; + p_dev->config_flags = CONF_ENABLE_IRQ; p_dev->config_index = 1; dev = alloc_etherdev(sizeof(struct wl3501_card)); @@ -1954,7 +1953,7 @@ static int wl3501_config(struct pcmcia_device *link) /* This actually configures the PCMCIA socket -- setting up the I/O * windows and the interrupt mapping. */ - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/parport/parport_cs.c b/drivers/parport/parport_cs.c index 8faf634987e3..63b3d3c18c34 100644 --- a/drivers/parport/parport_cs.c +++ b/drivers/parport/parport_cs.c @@ -48,7 +48,6 @@ #include #include -#include #include #include #include @@ -103,7 +102,7 @@ static int parport_probe(struct pcmcia_device *link) link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; return parport_config(link); } /* parport_attach */ @@ -172,7 +171,7 @@ static int parport_config(struct pcmcia_device *link) if (!link->irq) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/pcmcia/au1000_generic.h b/drivers/pcmcia/au1000_generic.h index 67530cefcf3c..5c36bda2963b 100644 --- a/drivers/pcmcia/au1000_generic.h +++ b/drivers/pcmcia/au1000_generic.h @@ -23,7 +23,6 @@ /* include the world */ -#include #include #include #include "cs_internal.h" diff --git a/drivers/pcmcia/au1000_pb1x00.c b/drivers/pcmcia/au1000_pb1x00.c index 807f2d75dad3..b2396647a165 100644 --- a/drivers/pcmcia/au1000_pb1x00.c +++ b/drivers/pcmcia/au1000_pb1x00.c @@ -31,7 +31,6 @@ #include #include -#include #include #include diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 91414a0ddc44..884a984216fe 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -28,7 +28,6 @@ #include #include -#include #include #include #include "cs_internal.h" diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 2ec8ac97445c..d8189d4061fc 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -33,7 +33,6 @@ #include #include -#include #include #include #include diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 42eef437afd4..7f1953f78b12 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -33,14 +33,9 @@ typedef struct config_t { struct kref ref; unsigned int state; - unsigned int Attributes; struct resource io[MAX_IO_WIN]; /* io ports */ struct resource mem[MAX_WIN]; /* mem areas */ - - struct { - u_int Attributes; - } irq; } config_t; diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 00db60053f93..dd43bd33a9e3 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -1178,7 +1177,7 @@ static int pcmcia_dev_resume(struct device *dev) if (p_dev->device_no == p_dev->func) { dev_dbg(dev, "requesting configuration\n"); - ret = pcmcia_request_configuration(p_dev, &p_dev->conf); + ret = pcmcia_enable_device(p_dev); if (ret) goto out; } diff --git a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c index 05d0879ce935..fc7906eaf228 100644 --- a/drivers/pcmcia/i82092.c +++ b/drivers/pcmcia/i82092.c @@ -16,7 +16,6 @@ #include #include -#include #include #include diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c index 61746bd598b3..72a033a2acdb 100644 --- a/drivers/pcmcia/i82365.c +++ b/drivers/pcmcia/i82365.c @@ -51,7 +51,6 @@ #include #include -#include #include diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c index 24de49925863..2adb0106a039 100644 --- a/drivers/pcmcia/m32r_cfc.c +++ b/drivers/pcmcia/m32r_cfc.c @@ -27,7 +27,6 @@ #include #include -#include #undef MAX_IO_WIN /* FIXME */ #define MAX_IO_WIN 1 diff --git a/drivers/pcmcia/m32r_pcc.c b/drivers/pcmcia/m32r_pcc.c index 8e4723844ad3..1511ff71c87b 100644 --- a/drivers/pcmcia/m32r_pcc.c +++ b/drivers/pcmcia/m32r_pcc.c @@ -28,7 +28,6 @@ #include #include -#include /* XXX: should be moved into asm/irq.h */ #define PCC0_IRQ 24 diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c index f0ecad99ce81..99d4f23cb435 100644 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ b/drivers/pcmcia/m8xx_pcmcia.c @@ -59,7 +59,6 @@ #include #include -#include #include #define pcmcia_info(args...) printk(KERN_INFO "m8xx_pcmcia: "args) diff --git a/drivers/pcmcia/pcmcia_cis.c b/drivers/pcmcia/pcmcia_cis.c index ac47cc4e20e8..ce8b94a3b675 100644 --- a/drivers/pcmcia/pcmcia_cis.c +++ b/drivers/pcmcia/pcmcia_cis.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "cs_internal.h" diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index a1fb0dc1a71f..28de5e6e164c 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -230,7 +229,7 @@ EXPORT_SYMBOL(pcmcia_map_mem_page); * pcmcia_fixup_iowidth() - reduce io width to 8bit * * pcmcia_fixup_iowidth() allows a PCMCIA device driver to reduce the - * IO width to 8bit after having called pcmcia_request_configuration() + * IO width to 8bit after having called pcmcia_enable_device() * previously. */ int pcmcia_fixup_iowidth(struct pcmcia_device *p_dev) @@ -278,7 +277,7 @@ EXPORT_SYMBOL(pcmcia_fixup_iowidth); * pcmcia_fixup_vpp() - set Vpp to a new voltage level * * pcmcia_fixup_vpp() allows a PCMCIA device driver to set Vpp to - * a new voltage level between calls to pcmcia_request_configuration() + * a new voltage level between calls to pcmcia_enable_device() * and pcmcia_disable_device(). */ int pcmcia_fixup_vpp(struct pcmcia_device *p_dev, unsigned char new_vpp) @@ -432,18 +431,21 @@ int pcmcia_release_window(struct pcmcia_device *p_dev, struct resource *res) } /* pcmcia_release_window */ EXPORT_SYMBOL(pcmcia_release_window); - -int pcmcia_request_configuration(struct pcmcia_device *p_dev, - config_req_t *req) +/** + * pcmcia_enable_device() - set up and activate a PCMCIA device + * + */ +int pcmcia_enable_device(struct pcmcia_device *p_dev) { int i; - u_int base; + unsigned int base; struct pcmcia_socket *s = p_dev->socket; config_t *c; pccard_io_map iomap; unsigned char status = 0; unsigned char ext_status = 0; unsigned char option = 0; + unsigned int flags = p_dev->config_flags; if (!(s->state & SOCKET_PRESENT)) return -ENODEV; @@ -466,23 +468,20 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, } /* Pick memory or I/O card, DMA mode, interrupt */ - c->Attributes = req->Attributes; if (p_dev->_io) s->socket.flags |= SS_IOCARD; - if (req->Attributes & CONF_ENABLE_DMA) - s->socket.flags |= SS_DMA_MODE; - if (req->Attributes & CONF_ENABLE_SPKR) { + if (flags & CONF_ENABLE_SPKR) { s->socket.flags |= SS_SPKR_ENA; status = CCSR_AUDIO_ENA; if (!(p_dev->config_regs & PRESENT_STATUS)) dev_warn(&p_dev->dev, "speaker requested, but " "PRESENT_STATUS not set!\n"); } - if (req->Attributes & CONF_ENABLE_IRQ) + if (flags & CONF_ENABLE_IRQ) s->socket.io_irq = s->pcmcia_irq; else s->socket.io_irq = 0; - if (req->Attributes & CONF_ENABLE_ESR) { + if (flags & CONF_ENABLE_ESR) { p_dev->config_regs |= PRESENT_EXT_STATUS; ext_status = ESR_REQ_ATTN_ENA; } @@ -510,8 +509,8 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, if (p_dev->config_regs & PRESENT_IOBASE_0) option |= COR_ADDR_DECODE; } - if ((req->Attributes & CONF_ENABLE_IRQ) && - !(req->Attributes & CONF_ENABLE_PULSE_IRQ)) + if ((flags & CONF_ENABLE_IRQ) && + !(flags & CONF_ENABLE_PULSE_IRQ)) option |= COR_LEVEL_REQ; pcmcia_write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &option); mdelay(40); @@ -560,8 +559,8 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, p_dev->_locked = 1; mutex_unlock(&s->ops_mutex); return 0; -} /* pcmcia_request_configuration */ -EXPORT_SYMBOL(pcmcia_request_configuration); +} /* pcmcia_enable_device */ +EXPORT_SYMBOL(pcmcia_enable_device); /** diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c index deef6656ab7b..8cbfa067171f 100644 --- a/drivers/pcmcia/pd6729.c +++ b/drivers/pcmcia/pd6729.c @@ -18,7 +18,6 @@ #include #include -#include #include diff --git a/drivers/pcmcia/rsrc_iodyn.c b/drivers/pcmcia/rsrc_iodyn.c index 8510c35d2952..523eb691c30b 100644 --- a/drivers/pcmcia/rsrc_iodyn.c +++ b/drivers/pcmcia/rsrc_iodyn.c @@ -17,7 +17,6 @@ #include #include -#include #include #include "cs_internal.h" diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index 4e80421fd908..aa628ed0e9f4 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -17,7 +17,6 @@ #include #include -#include #include #include "cs_internal.h" diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 96f348b35fde..b187555d4388 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -29,7 +29,6 @@ #include #include -#include #include #include "cs_internal.h" diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c index e09851480295..945857f8c284 100644 --- a/drivers/pcmcia/sa1100_generic.c +++ b/drivers/pcmcia/sa1100_generic.c @@ -35,7 +35,6 @@ #include #include -#include #include #include diff --git a/drivers/pcmcia/soc_common.h b/drivers/pcmcia/soc_common.h index 3fba3a679128..bbcd5385a221 100644 --- a/drivers/pcmcia/soc_common.h +++ b/drivers/pcmcia/soc_common.h @@ -11,7 +11,6 @@ /* include the world */ #include -#include #include #include diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index cb0d3ace18bd..71aeed93037c 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -27,7 +27,6 @@ #include #include -#include #include #include #include diff --git a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c index be0d841c7ebd..310160bffe38 100644 --- a/drivers/pcmcia/tcic.c +++ b/drivers/pcmcia/tcic.c @@ -49,7 +49,6 @@ #include #include -#include #include #include "tcic.h" diff --git a/drivers/pcmcia/xxs1500_ss.c b/drivers/pcmcia/xxs1500_ss.c index fa88c360c37a..3b67a1b6a197 100644 --- a/drivers/pcmcia/xxs1500_ss.c +++ b/drivers/pcmcia/xxs1500_ss.c @@ -17,7 +17,6 @@ #include #include -#include #include #include diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 414d9a6f9a32..408dbaa080a1 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -20,7 +20,6 @@ #include #include -#include #include "yenta_socket.h" #include "i82365.h" diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c index c3682492af16..e1f748517135 100644 --- a/drivers/scsi/pcmcia/aha152x_stub.c +++ b/drivers/scsi/pcmcia/aha152x_stub.c @@ -49,7 +49,6 @@ #include #include "aha152x.h" -#include #include #include @@ -102,7 +101,7 @@ static int aha152x_probe(struct pcmcia_device *link) link->resource[0]->end = 0x20; link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; link->config_regs = PRESENT_OPTION; return aha152x_config_cs(link); @@ -159,7 +158,7 @@ static int aha152x_config_cs(struct pcmcia_device *link) if (!link->irq) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c index bb909e1b7c68..ae263b17bfa5 100644 --- a/drivers/scsi/pcmcia/fdomain_stub.c +++ b/drivers/scsi/pcmcia/fdomain_stub.c @@ -46,7 +46,6 @@ #include #include "fdomain.h" -#include #include #include @@ -85,7 +84,7 @@ static int fdomain_probe(struct pcmcia_device *link) link->priv = info; link->resource[0]->end = 0x10; link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; link->config_regs = PRESENT_OPTION; return fdomain_config(link); @@ -131,7 +130,7 @@ static int fdomain_config(struct pcmcia_device *link) if (!link->irq) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c index a5648e9c4f6e..d0546c03f57c 100644 --- a/drivers/scsi/pcmcia/nsp_cs.c +++ b/drivers/scsi/pcmcia/nsp_cs.c @@ -47,7 +47,6 @@ #include #include -#include #include #include #include @@ -1562,7 +1561,7 @@ static int nsp_cs_probe(struct pcmcia_device *link) link->resource[0]->flags = IO_DATA_PATH_WIDTH_AUTO; /* General socket configuration */ - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; ret = nsp_cs_config(link); @@ -1608,7 +1607,7 @@ static int nsp_cs_config_check(struct pcmcia_device *p_dev, /* Does this card need audio output? */ if (cfg->flags & CISTPL_CFTABLE_AUDIO) - p_dev->conf.Attributes |= CONF_ENABLE_SPKR; + p_dev->config_flags |= CONF_ENABLE_SPKR; /* Use power settings for Vcc and Vpp if present */ /* Note that the CIS values need to be rescaled */ @@ -1629,7 +1628,7 @@ static int nsp_cs_config_check(struct pcmcia_device *p_dev, } /* Do we need to allocate an interrupt? */ - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->resource[0]->end = p_dev->resource[1]->end = 0; @@ -1700,7 +1699,7 @@ static int nsp_cs_config(struct pcmcia_device *link) if (pcmcia_request_irq(link, nspintr)) goto cs_failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto cs_failed; @@ -1749,9 +1748,7 @@ static int nsp_cs_config(struct pcmcia_device *link) if (link->vpp) { printk(", Vpp %d.%d", link->vpp/10, link->vpp%10); } - if (link->conf.Attributes & CONF_ENABLE_IRQ) { - printk(", irq %d", link->irq); - } + printk(", irq %d", link->irq); if (link->resource[0]) printk(", io %pR", link->resource[0]); if (link->resource[1]) diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c index 5e2cbe091408..7d3f49c431fd 100644 --- a/drivers/scsi/pcmcia/qlogic_stub.c +++ b/drivers/scsi/pcmcia/qlogic_stub.c @@ -48,7 +48,6 @@ #include #include "../qlogicfas408.h" -#include #include #include #include @@ -158,7 +157,7 @@ static int qlogic_probe(struct pcmcia_device *link) link->priv = info; link->resource[0]->end = 16; link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; link->config_regs = PRESENT_OPTION; return qlogic_config(link); @@ -208,7 +207,7 @@ static int qlogic_config(struct pcmcia_device * link) if (!link->irq) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; @@ -263,7 +262,7 @@ static int qlogic_resume(struct pcmcia_device *link) { scsi_info_t *info = link->priv; - pcmcia_request_configuration(link, &link->conf); + pcmcia_enable_device(link); if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) { diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c index 9aaf974d4d1c..600630eb7034 100644 --- a/drivers/scsi/pcmcia/sym53c500_cs.c +++ b/drivers/scsi/pcmcia/sym53c500_cs.c @@ -71,7 +71,6 @@ #include #include -#include #include #include #include @@ -721,7 +720,7 @@ SYM53C500_config(struct pcmcia_device *link) if (!link->irq) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; @@ -861,7 +860,7 @@ SYM53C500_probe(struct pcmcia_device *link) link->priv = info; link->resource[0]->end = 16; link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; return SYM53C500_config(link); } /* SYM53C500_attach */ diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index ee19f2d25c20..47b1869026e7 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -45,7 +45,6 @@ #include #include -#include #include #include #include @@ -184,7 +183,7 @@ static void quirk_config_socket(struct pcmcia_device *link) struct serial_info *info = link->priv; if (info->multi) - link->conf.Attributes |= CONF_ENABLE_ESR; + link->config_flags |= CONF_ENABLE_ESR; } static const struct serial_quirk quirks[] = { @@ -333,9 +332,9 @@ static int serial_probe(struct pcmcia_device *link) info->p_dev = link; link->priv = info; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; if (do_sound) - link->conf.Attributes |= CONF_ENABLE_SPKR; + link->config_flags |= CONF_ENABLE_SPKR; return serial_config(link); } @@ -503,7 +502,7 @@ found_port: if (info->quirk && info->quirk->config) info->quirk->config(link); - i = pcmcia_request_configuration(link, &link->conf); + i = pcmcia_enable_device(link); if (i != 0) return -1; return setup_serial(link, info, link->resource[0]->start, link->irq); @@ -579,7 +578,7 @@ static int multi_config(struct pcmcia_device *link) if (info->quirk && info->quirk->config) info->quirk->config(link); - i = pcmcia_request_configuration(link, &link->conf); + i = pcmcia_enable_device(link); if (i != 0) return -ENODEV; diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 7892ac163522..c68b3dc19e11 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -20,7 +20,6 @@ #include #include -#include #include #include diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c index 526682d68de8..c7345dbf43fa 100644 --- a/drivers/ssb/pcmcia.c +++ b/drivers/ssb/pcmcia.c @@ -13,7 +13,6 @@ #include #include -#include #include #include #include diff --git a/drivers/ssb/scan.c b/drivers/ssb/scan.c index 9738cad4ba13..ee079ab9fb28 100644 --- a/drivers/ssb/scan.c +++ b/drivers/ssb/scan.c @@ -17,7 +17,6 @@ #include #include -#include #include #include diff --git a/drivers/staging/comedi/drivers/cb_das16_cs.c b/drivers/staging/comedi/drivers/cb_das16_cs.c index 034cbfcba196..ee91c89511ed 100644 --- a/drivers/staging/comedi/drivers/cb_das16_cs.c +++ b/drivers/staging/comedi/drivers/cb_das16_cs.c @@ -37,7 +37,6 @@ Status: experimental #include #include -#include #include #include @@ -692,9 +691,6 @@ static int das16cs_pcmcia_attach(struct pcmcia_device *link) local->link = link; link->priv = local; - /* Initialize the pcmcia_device structure */ - link->conf.Attributes = 0; - cur_dev = link; das16cs_pcmcia_config(link); @@ -723,7 +719,7 @@ static int das16cs_pcmcia_config_loop(struct pcmcia_device *p_dev, return -EINVAL; /* Do we need to allocate an interrupt? */ - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->resource[0]->end = p_dev->resource[1]->end = 0; @@ -767,14 +763,13 @@ static void das16cs_pcmcia_config(struct pcmcia_device *link) the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. */ - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; /* Finally, report what we've done */ dev_info(&link->dev, "index 0x%02x", link->config_index); - if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %u", link->irq); + printk(", irq %u", link->irq); if (link->resource[0]) printk(", io %pR", link->resource[0]); if (link->resource[1]) diff --git a/drivers/staging/comedi/drivers/das08_cs.c b/drivers/staging/comedi/drivers/das08_cs.c index e37ea79e6aea..f8f3de57b413 100644 --- a/drivers/staging/comedi/drivers/das08_cs.c +++ b/drivers/staging/comedi/drivers/das08_cs.c @@ -48,7 +48,6 @@ Command support does not exist, but could be added for this board. #include "das08.h" /* pcmcia includes */ -#include #include #include @@ -162,15 +161,6 @@ static int das08_pcmcia_attach(struct pcmcia_device *link) local->link = link; link->priv = local; - /* - General socket configuration defaults can go here. In this - client, we assume very little, and rely on the CIS for almost - everything. In most clients, many details (i.e., number, sizes, - and attributes of IO windows) are fixed by the nature of the - device, and can be hard-wired here. - */ - link->conf.Attributes = 0; - cur_dev = link; das08_pcmcia_config(link); @@ -211,7 +201,7 @@ static int das08_pcmcia_config_loop(struct pcmcia_device *p_dev, return -ENODEV; /* Do we need to allocate an interrupt? */ - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->resource[0]->end = p_dev->resource[1]->end = 0; @@ -263,14 +253,13 @@ static void das08_pcmcia_config(struct pcmcia_device *link) the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. */ - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; /* Finally, report what we've done */ dev_info(&link->dev, "index 0x%02x", link->config_index); - if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %u", link->irq); + printk(", irq %u", link->irq); if (link->resource[0]) printk(", io %pR", link->resource[0]); if (link->resource[1]) diff --git a/drivers/staging/comedi/drivers/ni_daq_700.c b/drivers/staging/comedi/drivers/ni_daq_700.c index 8107e4009248..803683b83543 100644 --- a/drivers/staging/comedi/drivers/ni_daq_700.c +++ b/drivers/staging/comedi/drivers/ni_daq_700.c @@ -47,7 +47,6 @@ IRQ is assigned but not used. #include -#include #include #include #include @@ -491,15 +490,6 @@ static int dio700_cs_attach(struct pcmcia_device *link) local->link = link; link->priv = local; - /* - General socket configuration defaults can go here. In this - client, we assume very little, and rely on the CIS for almost - everything. In most clients, many details (i.e., number, sizes, - and attributes of IO windows) are fixed by the nature of the - device, and can be hard-wired here. - */ - link->conf.Attributes = 0; - pcmcia_cur_dev = link; dio700_config(link); @@ -550,10 +540,10 @@ static int dio700_pcmcia_config_loop(struct pcmcia_device *p_dev, /* Does this card need audio output? */ if (cfg->flags & CISTPL_CFTABLE_AUDIO) - p_dev->conf.Attributes |= CONF_ENABLE_SPKR; + p_dev->config_flags |= CONF_ENABLE_SPKR; /* Do we need to allocate an interrupt? */ - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->resource[0]->end = p_dev->resource[1]->end = 0; @@ -602,14 +592,13 @@ static void dio700_config(struct pcmcia_device *link) the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. */ - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret != 0) goto failed; /* Finally, report what we've done */ dev_info(&link->dev, "index 0x%02x", link->config_index); - if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq); + printk(", irq %d", link->irq); if (link->resource[0]) printk(", io %pR", link->resource[0]); if (link->resource[1]) diff --git a/drivers/staging/comedi/drivers/ni_daq_dio24.c b/drivers/staging/comedi/drivers/ni_daq_dio24.c index 4f9daa3558aa..6512f7a283ce 100644 --- a/drivers/staging/comedi/drivers/ni_daq_dio24.c +++ b/drivers/staging/comedi/drivers/ni_daq_dio24.c @@ -48,7 +48,6 @@ the PCMCIA interface. #include "8255.h" -#include #include #include #include @@ -243,15 +242,6 @@ static int dio24_cs_attach(struct pcmcia_device *link) local->link = link; link->priv = local; - /* - General socket configuration defaults can go here. In this - client, we assume very little, and rely on the CIS for almost - everything. In most clients, many details (i.e., number, sizes, - and attributes of IO windows) are fixed by the nature of the - device, and can be hard-wired here. - */ - link->conf.Attributes = 0; - pcmcia_cur_dev = link; dio24_config(link); @@ -302,10 +292,10 @@ static int dio24_pcmcia_config_loop(struct pcmcia_device *p_dev, /* Does this card need audio output? */ if (cfg->flags & CISTPL_CFTABLE_AUDIO) - p_dev->conf.Attributes |= CONF_ENABLE_SPKR; + p_dev->config_flags |= CONF_ENABLE_SPKR; /* Do we need to allocate an interrupt? */ - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->resource[0]->end = p_dev->resource[1]->end = 0; @@ -353,14 +343,13 @@ static void dio24_config(struct pcmcia_device *link) the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. */ - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; /* Finally, report what we've done */ dev_info(&link->dev, "index 0x%02x", link->config_index); - if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq); + printk(", irq %d", link->irq); if (link->resource[0]) printk(" & %pR", link->resource[0]); if (link->resource[1]) diff --git a/drivers/staging/comedi/drivers/ni_labpc_cs.c b/drivers/staging/comedi/drivers/ni_labpc_cs.c index 2e27a3048cb8..255cf40c5fd1 100644 --- a/drivers/staging/comedi/drivers/ni_labpc_cs.c +++ b/drivers/staging/comedi/drivers/ni_labpc_cs.c @@ -71,7 +71,6 @@ NI manuals: #include "comedi_fc.h" #include "ni_labpc.h" -#include #include #include #include @@ -219,15 +218,6 @@ static int labpc_cs_attach(struct pcmcia_device *link) local->link = link; link->priv = local; - /* - General socket configuration defaults can go here. In this - client, we assume very little, and rely on the CIS for almost - everything. In most clients, many details (i.e., number, sizes, - and attributes of IO windows) are fixed by the nature of the - device, and can be hard-wired here. - */ - link->conf.Attributes = 0; - pcmcia_cur_dev = link; labpc_config(link); @@ -281,10 +271,10 @@ static int labpc_pcmcia_config_loop(struct pcmcia_device *p_dev, /* Does this card need audio output? */ if (cfg->flags & CISTPL_CFTABLE_AUDIO) - p_dev->conf.Attributes |= CONF_ENABLE_SPKR; + p_dev->config_flags |= CONF_ENABLE_SPKR; /* Do we need to allocate an interrupt? */ - p_dev->conf.Attributes |= CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ; /* IO window settings */ p_dev->resource[0]->end = p_dev->resource[1]->end = 0; @@ -331,14 +321,14 @@ static void labpc_config(struct pcmcia_device *link) the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. */ - ret = pcmcia_request_configuration(link, &link->conf); + p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ; + ret = pcmcia_enable_device(link); if (ret) goto failed; /* Finally, report what we've done */ dev_info(&link->dev, "index 0x%02x", link->config_index); - if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq); + printk(", irq %d", link->irq); if (link->resource[0]) printk(" & %pR", link->resource[0]); if (link->resource[1]) diff --git a/drivers/staging/comedi/drivers/ni_mio_cs.c b/drivers/staging/comedi/drivers/ni_mio_cs.c index be7e021e576d..b88f52dd08d0 100644 --- a/drivers/staging/comedi/drivers/ni_mio_cs.c +++ b/drivers/staging/comedi/drivers/ni_mio_cs.c @@ -48,7 +48,6 @@ See the notes in the ni_atmio.o driver. #include "ni_stc.h" #include "8255.h" -#include #include #include @@ -265,7 +264,7 @@ static int cs_attach(struct pcmcia_device *link) { link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; link->resource[0]->end = 16; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; cur_dev = link; @@ -336,7 +335,7 @@ static void mio_cs_config(struct pcmcia_device *link) if (!link->irq) dev_info(&link->dev, "no IRQ available\n"); - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); } static int mio_cs_attach(struct comedi_device *dev, struct comedi_devconfig *it) diff --git a/drivers/staging/comedi/drivers/quatech_daqp_cs.c b/drivers/staging/comedi/drivers/quatech_daqp_cs.c index a2a32de044c8..b8940d7f4155 100644 --- a/drivers/staging/comedi/drivers/quatech_daqp_cs.c +++ b/drivers/staging/comedi/drivers/quatech_daqp_cs.c @@ -50,7 +50,6 @@ Devices: [Quatech] DAQP-208 (daqp), DAQP-308 #include "../comedidev.h" #include -#include #include #include #include @@ -1031,15 +1030,6 @@ static int daqp_cs_attach(struct pcmcia_device *link) local->link = link; link->priv = local; - /* - General socket configuration defaults can go here. In this - client, we assume very little, and rely on the CIS for almost - everything. In most clients, many details (i.e., number, sizes, - and attributes of IO windows) are fixed by the nature of the - device, and can be hard-wired here. - */ - link->conf.Attributes = 0; - daqp_cs_config(link); return 0; @@ -1088,7 +1078,7 @@ static int daqp_pcmcia_config_loop(struct pcmcia_device *p_dev, return -ENODEV; /* Do we need to allocate an interrupt? */ - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->resource[0]->end = p_dev->resource[1]->end = 0; @@ -1132,14 +1122,13 @@ static void daqp_cs_config(struct pcmcia_device *link) the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. */ - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; /* Finally, report what we've done */ dev_info(&link->dev, "index 0x%02x", link->config_index); - if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %u", link->irq); + printk(", irq %u", link->irq); if (link->resource[0]) printk(" & %pR", link->resource[0]); if (link->resource[1]) diff --git a/drivers/staging/wlags49_h2/wl_cs.c b/drivers/staging/wlags49_h2/wl_cs.c index 778800f1e464..62a70afa3e29 100644 --- a/drivers/staging/wlags49_h2/wl_cs.c +++ b/drivers/staging/wlags49_h2/wl_cs.c @@ -83,7 +83,6 @@ #include #include -#include #include #include #include @@ -147,7 +146,7 @@ static int wl_adapter_attach(struct pcmcia_device *link) link->resource[0]->end = HCF_NUM_IO_PORTS; link->resource[0]->flags= IO_DATA_PATH_WIDTH_16; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; link->config_index = 5; link->config_regs = PRESENT_OPTION; @@ -301,7 +300,7 @@ void wl_adapter_insert(struct pcmcia_device *link) dev = link->priv; /* Do we need to allocate an interrupt? */ - link->conf.Attributes |= CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; link->io_lines = 6; ret = pcmcia_request_io(link); @@ -312,7 +311,7 @@ void wl_adapter_insert(struct pcmcia_device *link) if (ret != 0) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret != 0) goto failed; diff --git a/drivers/staging/wlags49_h2/wl_internal.h b/drivers/staging/wlags49_h2/wl_internal.h index 02f0a20e178a..cd129b3ee6c0 100644 --- a/drivers/staging/wlags49_h2/wl_internal.h +++ b/drivers/staging/wlags49_h2/wl_internal.h @@ -69,7 +69,6 @@ ******************************************************************************/ #include #ifdef BUS_PCMCIA -#include #include #include #include diff --git a/drivers/telephony/ixj_pcmcia.c b/drivers/telephony/ixj_pcmcia.c index 5ccc6d0560a9..e23270d1b4d1 100644 --- a/drivers/telephony/ixj_pcmcia.c +++ b/drivers/telephony/ixj_pcmcia.c @@ -8,7 +8,6 @@ #include /* error codes */ #include -#include #include #include @@ -143,7 +142,7 @@ static int ixj_config(struct pcmcia_device * link) if (pcmcia_loop_config(link, ixj_config_check, &dflt)) goto failed; - if (pcmcia_request_configuration(link, &link->conf)) + if (pcmcia_enable_device(link)) goto failed; /* diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c index 590405361bed..744c2cd809f1 100644 --- a/drivers/usb/host/sl811_cs.c +++ b/drivers/usb/host/sl811_cs.c @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -159,7 +158,7 @@ static int sl811_cs_config_check(struct pcmcia_device *p_dev, dflt->vpp1.param[CISTPL_POWER_VNOM]/10000; /* we need an interrupt */ - p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ; /* IO window settings */ p_dev->resource[0]->end = p_dev->resource[1]->end = 0; @@ -195,7 +194,7 @@ static int sl811_cs_config(struct pcmcia_device *link) if (!link->irq) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; @@ -227,8 +226,6 @@ static int sl811_cs_probe(struct pcmcia_device *link) local->p_dev = link; link->priv = local; - link->conf.Attributes = 0; - return sl811_cs_config(link); } diff --git a/include/pcmcia/cs.h b/include/pcmcia/cs.h deleted file mode 100644 index 47b6092c4ed6..000000000000 --- a/include/pcmcia/cs.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * cs.h - * - * 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. - * - * The initial developer of the original code is David A. Hinds - * . Portions created by David A. Hinds - * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - * - * (C) 1999 David A. Hinds - */ - -#ifndef _LINUX_CS_H -#define _LINUX_CS_H - -#ifdef __KERNEL__ -#include -#endif - -/* For RequestConfiguration */ -typedef struct config_req_t { - u_int Attributes; -} config_req_t; - -/* Attributes for RequestConfiguration */ -#define CONF_ENABLE_IRQ 0x01 -#define CONF_ENABLE_DMA 0x02 -#define CONF_ENABLE_SPKR 0x04 -#define CONF_ENABLE_PULSE_IRQ 0x08 -#define CONF_ENABLE_ESR 0x10 -#define CONF_VALID_CLIENT 0x100 - -#endif /* _LINUX_CS_H */ diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h index bc28f96d0b5a..50b03fd67fd6 100644 --- a/include/pcmcia/ds.h +++ b/include/pcmcia/ds.h @@ -24,9 +24,11 @@ #ifdef __KERNEL__ #include +#include #include #include + /* * PCMCIA device drivers (16-bit cards only; 32-bit cards require CardBus * a.k.a. PCI drivers @@ -88,18 +90,16 @@ struct pcmcia_device { struct list_head socket_device_list; - /* deprecated, will be cleaned up soon */ - config_req_t conf; - /* device setup */ unsigned int irq; struct resource *resource[PCMCIA_NUM_RESOURCES]; unsigned int vpp; - unsigned int io_lines; /* number of I/O lines */ + unsigned int config_flags; /* CONF_ENABLE_ flags below */ unsigned int config_base; unsigned int config_index; unsigned int config_regs; /* PRESENT_ flags below */ + unsigned int io_lines; /* number of I/O lines */ /* Is the device suspended? */ u16 suspended:1; @@ -207,8 +207,7 @@ pcmcia_request_exclusive_irq(struct pcmcia_device *p_dev, int __must_check pcmcia_request_irq(struct pcmcia_device *p_dev, irq_handler_t handler); -int pcmcia_request_configuration(struct pcmcia_device *p_dev, - config_req_t *req); +int pcmcia_enable_device(struct pcmcia_device *p_dev); int pcmcia_request_window(struct pcmcia_device *p_dev, struct resource *res, unsigned int speed); @@ -265,6 +264,12 @@ static inline int pcmcia_io_cfg_data_width(unsigned int flags) #define PRESENT_IOBASE_3 0x100 #define PRESENT_IOSIZE 0x200 +/* flags to be passed to pcmcia_enable_device() */ +#define CONF_ENABLE_IRQ 0x01 +#define CONF_ENABLE_SPKR 0x02 +#define CONF_ENABLE_PULSE_IRQ 0x04 +#define CONF_ENABLE_ESR 0x08 + #endif /* __KERNEL__ */ #endif /* _LINUX_DS_H */ diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h index 626b63c33d9e..731cde010f42 100644 --- a/include/pcmcia/ss.h +++ b/include/pcmcia/ss.h @@ -19,7 +19,6 @@ #include /* task_struct, completion */ #include -#include #ifdef CONFIG_CARDBUS #include #endif diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c index 4df07fce637f..2476d5f0a14f 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c @@ -142,7 +142,7 @@ static int snd_pdacf_probe(struct pcmcia_device *link) link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; link->resource[0]->end = 16; - link->conf.Attributes = CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ; + link->config_flags = CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ; link->config_index = 1; link->config_regs = PRESENT_OPTION; @@ -217,6 +217,7 @@ static int pdacf_config(struct pcmcia_device *link) snd_printdd(KERN_DEBUG "pdacf_config called\n"); link->config_index = 0x5; + link->config_flags |= CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ; ret = pcmcia_request_io(link); if (ret) @@ -226,7 +227,7 @@ static int pdacf_config(struct pcmcia_device *link) if (ret) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.h b/sound/pcmcia/pdaudiocf/pdaudiocf.h index 5cc3e4573074..bd26e092aead 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf.h +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.h @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index 16186adc1bdc..017a8d6c510d 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -2,7 +2,7 @@ * Driver for Digigram VXpocket V2/440 soundcards * * Copyright (c) 2002 by Takashi Iwai - * + * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -162,7 +162,7 @@ static int snd_vxpocket_new(struct snd_card *card, int ibl, link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; link->resource[0]->end = 16; - link->conf.Attributes = CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ; link->config_index = 1; link->config_regs = PRESENT_OPTION; @@ -233,7 +233,7 @@ static int vxpocket_config(struct pcmcia_device *link) if (ret) goto failed; - ret = pcmcia_request_configuration(link, &link->conf); + ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/sound/pcmcia/vx/vxpocket.h b/sound/pcmcia/vx/vxpocket.h index d9110669d042..13d658c1a216 100644 --- a/sound/pcmcia/vx/vxpocket.h +++ b/sound/pcmcia/vx/vxpocket.h @@ -23,7 +23,6 @@ #include -#include #include #include -- cgit v1.2.3-70-g09d2 From 00990e7ce0b0e596fe41d9c64d6933ea70084003 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Fri, 30 Jul 2010 13:13:46 +0200 Subject: pcmcia: use autoconfiguration feature for ioports and iomem When CONF_AUTO_SET_IO or CONF_AUTO_SET_IOMEM are set, the corresponding fields in struct pcmcia_device *p_dev->resource[0,1,2] are set accordinly. Drivers wishing to override certain settings may do so in the callback function, but they no longer need to parse the CIS entries stored in cistpl_cftable_entry_t themselves. CC: netdev@vger.kernel.org CC: linux-wireless@vger.kernel.org CC: linux-ide@vger.kernel.org CC: linux-usb@vger.kernel.org CC: laforge@gnumonks.org CC: linux-mtd@lists.infradead.org CC: linux-bluetooth@vger.kernel.org CC: alsa-devel@alsa-project.org CC: linux-serial@vger.kernel.org CC: Jiri Kosina CC: linux-scsi@vger.kernel.org Tested-by: Wolfram Sang Signed-off-by: Dominik Brodowski --- Documentation/pcmcia/driver-changes.txt | 12 +++ drivers/ata/pata_pcmcia.c | 81 +++++--------- drivers/bluetooth/bt3c_cs.c | 53 ++++----- drivers/bluetooth/btuart_cs.c | 51 ++++----- drivers/bluetooth/dtl1_cs.c | 18 ++-- drivers/char/pcmcia/cm4000_cs.c | 15 +-- drivers/char/pcmcia/cm4040_cs.c | 22 +--- drivers/char/pcmcia/ipwireless/main.c | 26 ++--- drivers/char/pcmcia/synclink_cs.c | 16 +-- drivers/ide/ide-cs.c | 82 +++++--------- drivers/isdn/hardware/avm/avm_cs.c | 19 +--- drivers/isdn/hisax/avma1_cs.c | 22 ++-- drivers/isdn/hisax/elsa_cs.c | 23 ++-- drivers/isdn/hisax/sedlbauer_cs.c | 36 ++----- drivers/isdn/hisax/teles_cs.c | 16 ++- drivers/net/pcmcia/axnet_cs.c | 32 ++---- drivers/net/pcmcia/fmvj18x_cs.c | 5 +- drivers/net/pcmcia/pcnet_cs.c | 38 ++----- drivers/net/pcmcia/smc91c92_cs.c | 39 ++++--- drivers/net/pcmcia/xirc2ps_cs.c | 69 ++++++------ drivers/net/wireless/airo_cs.c | 34 +----- drivers/net/wireless/atmel_cs.c | 28 +---- drivers/net/wireless/hostap/hostap_cs.c | 43 +------- drivers/net/wireless/libertas/if_cs.c | 16 +-- drivers/net/wireless/orinoco/orinoco_cs.c | 41 ++----- drivers/net/wireless/orinoco/spectrum_cs.c | 45 +------- drivers/parport/parport_cs.c | 34 ++---- drivers/pcmcia/pcmcia_cis.c | 78 ++++++++++++-- drivers/scsi/pcmcia/aha152x_stub.c | 34 +++--- drivers/scsi/pcmcia/fdomain_stub.c | 13 +-- drivers/scsi/pcmcia/nsp_cs.c | 47 ++------ drivers/scsi/pcmcia/qlogic_stub.c | 13 +-- drivers/scsi/pcmcia/sym53c500_cs.c | 13 +-- drivers/serial/serial_cs.c | 132 ++++++++++++----------- drivers/staging/comedi/drivers/cb_das16_cs.c | 31 +----- drivers/staging/comedi/drivers/das08_cs.c | 33 ++---- drivers/staging/comedi/drivers/ni_daq_700.c | 32 +----- drivers/staging/comedi/drivers/ni_daq_dio24.c | 32 +----- drivers/staging/comedi/drivers/ni_labpc_cs.c | 32 +----- drivers/staging/comedi/drivers/ni_mio_cs.c | 14 +-- drivers/staging/comedi/drivers/quatech_daqp_cs.c | 32 +----- drivers/telephony/ixj_pcmcia.c | 32 ++---- drivers/usb/host/sl811_cs.c | 26 +---- include/pcmcia/ds.h | 30 ++---- 44 files changed, 515 insertions(+), 1025 deletions(-) (limited to 'Documentation') diff --git a/Documentation/pcmcia/driver-changes.txt b/Documentation/pcmcia/driver-changes.txt index 62a029f24f46..dd04361dd361 100644 --- a/Documentation/pcmcia/driver-changes.txt +++ b/Documentation/pcmcia/driver-changes.txt @@ -1,4 +1,16 @@ This file details changes in 2.6 which affect PCMCIA card driver authors: +* pcmcia_loop_config() and autoconfiguration (as of 2.6.36) + If struct pcmcia_device *p_dev->config_flags is set accordingly, + pcmcia_loop_config() now sets up certain configuration values + automatically, though the driver may still override the settings + in the callback function. The following autoconfiguration options + are provided at the moment: + CONF_AUTO_CHECK_VCC : check for matching Vcc + CONF_AUTO_SET_VPP : set Vpp + CONF_AUTO_AUDIO : auto-enable audio line, if required + CONF_AUTO_SET_IO : set ioport resources (->resource[0,1]) + CONF_AUTO_SET_IOMEM : set first iomem resource (->resource[2]) + * pcmcia_request_configuration -> pcmcia_enable_device (as of 2.6.36) pcmcia_request_configuration() got renamed to pcmcia_enable_device(), as it mirrors pcmcia_disable_device(). Configuration settings are now diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index 954f43c512f1..88cb03c36963 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -167,45 +167,26 @@ static struct ata_port_operations pcmcia_8bit_port_ops = { }; -struct pcmcia_config_check { - unsigned long ctl_base; - int is_kme; -}; - -static int pcmcia_check_one_config(struct pcmcia_device *pdev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int pcmcia_check_one_config(struct pcmcia_device *pdev, void *priv_data) { - struct pcmcia_config_check *stk = priv_data; - - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - pdev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - pdev->resource[0]->start = io->win[0].base; - if (!(io->flags & CISTPL_IO_16BIT)) { - pdev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - pdev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - } - if (io->nwin == 2) { - pdev->resource[0]->end = 8; - pdev->resource[1]->start = io->win[1].base; - pdev->resource[1]->end = (stk->is_kme) ? 2 : 1; - if (pcmcia_request_io(pdev) != 0) - return -ENODEV; - stk->ctl_base = pdev->resource[1]->start; - } else if ((io->nwin == 1) && (io->win[0].len >= 16)) { - pdev->resource[0]->end = io->win[0].len; - pdev->resource[1]->end = 0; - if (pcmcia_request_io(pdev) != 0) - return -ENODEV; - stk->ctl_base = pdev->resource[0]->start + 0x0e; - } else + int *is_kme = priv_data; + + if (!(pdev->resource[0]->flags & IO_DATA_PATH_WIDTH_8)) { + pdev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + pdev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + } + pdev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; + pdev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; + + if (pdev->resource[1]->end) { + pdev->resource[0]->end = 8; + pdev->resource[1]->end = (*is_kme) ? 2 : 1; + } else { + if (pdev->resource[0]->end < 16) return -ENODEV; - /* If we've got this far, we're done */ - return 0; } - return -ENODEV; + + return pcmcia_request_io(pdev); } /** @@ -220,7 +201,6 @@ static int pcmcia_init_one(struct pcmcia_device *pdev) { struct ata_host *host; struct ata_port *ap; - struct pcmcia_config_check *stk = NULL; int is_kme = 0, ret = -ENOMEM, p; unsigned long io_base, ctl_base; void __iomem *io_addr, *ctl_addr; @@ -228,10 +208,8 @@ static int pcmcia_init_one(struct pcmcia_device *pdev) struct ata_port_operations *ops = &pcmcia_port_ops; /* Set up attributes in order to probe card and get resources */ - pdev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - pdev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - pdev->config_flags |= CONF_ENABLE_IRQ; - pdev->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC; + pdev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO | + CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC; /* See if we have a manufacturer identifier. Use it to set is_kme for vendor quirks */ @@ -239,21 +217,17 @@ static int pcmcia_init_one(struct pcmcia_device *pdev) ((pdev->card_id == PRODID_KME_KXLC005_A) || (pdev->card_id == PRODID_KME_KXLC005_B))); - /* Allocate resoure probing structures */ - - stk = kzalloc(sizeof(*stk), GFP_KERNEL); - if (!stk) - goto out1; - stk->is_kme = is_kme; - io_base = ctl_base = 0; - - if (pcmcia_loop_config(pdev, pcmcia_check_one_config, stk)) { + if (pcmcia_loop_config(pdev, pcmcia_check_one_config, &is_kme)) { pdev->config_flags &= ~CONF_AUTO_CHECK_VCC; - if (pcmcia_loop_config(pdev, pcmcia_check_one_config, stk)) + if (pcmcia_loop_config(pdev, pcmcia_check_one_config, &is_kme)) goto failed; /* No suitable config found */ } io_base = pdev->resource[0]->start; - ctl_base = stk->ctl_base; + if (pdev->resource[1]->end) + ctl_base = pdev->resource[1]->start; + else + ctl_base = pdev->resource[0]->start + 0x0e; + if (!pdev->irq) goto failed; @@ -310,13 +284,10 @@ static int pcmcia_init_one(struct pcmcia_device *pdev) goto failed; pdev->priv = host; - kfree(stk); return 0; failed: - kfree(stk); pcmcia_disable_device(pdev); -out1: return ret; } diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 97338a3aae1a..8b8be35fe312 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -656,10 +656,8 @@ static int bt3c_probe(struct pcmcia_device *link) info->p_dev = link; link->priv = info; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - link->resource[0]->end = 8; - - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | + CONF_AUTO_SET_IO; return bt3c_config(link); } @@ -673,38 +671,41 @@ static void bt3c_detach(struct pcmcia_device *link) kfree(info); } -static int bt3c_check_config(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int bt3c_check_config(struct pcmcia_device *p_dev, void *priv_data) { - unsigned long try = (unsigned long) priv_data; - p_dev->io_lines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; + int *try = priv_data; - if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && - (cf->io.win[0].base != 0)) { - p_dev->resource[0]->start = cf->io.win[0].base; - if (!pcmcia_request_io(p_dev)) - return 0; - } - return -ENODEV; + if (try == 0) + p_dev->io_lines = 16; + + if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0)) + return -EINVAL; + + p_dev->resource[0]->end = 8; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + + return pcmcia_request_io(p_dev); } static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, void *priv_data) { static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; int j; - if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { - for (j = 0; j < 5; j++) { - p_dev->resource[0]->start = base[j]; - p_dev->io_lines = base[j] ? 16 : 3; - if (!pcmcia_request_io(p_dev)) - return 0; - } + if (p_dev->io_lines > 3) + return -ENODEV; + + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = 8; + + for (j = 0; j < 5; j++) { + p_dev->resource[0]->start = base[j]; + p_dev->io_lines = base[j] ? 16 : 3; + if (!pcmcia_request_io(p_dev)) + return 0; } return -ENODEV; } diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 8a6864fc8c38..9f9bb69dc0a2 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -585,10 +585,8 @@ static int btuart_probe(struct pcmcia_device *link) info->p_dev = link; link->priv = info; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - link->resource[0]->end = 8; - - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | + CONF_AUTO_SET_IO; return btuart_config(link); } @@ -602,38 +600,41 @@ static void btuart_detach(struct pcmcia_device *link) kfree(info); } -static int btuart_check_config(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int btuart_check_config(struct pcmcia_device *p_dev, void *priv_data) { int *try = priv_data; - p_dev->io_lines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; - if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && - (cf->io.win[0].base != 0)) { - p_dev->resource[0]->start = cf->io.win[0].base; - if (!pcmcia_request_io(p_dev)) - return 0; - } - return -ENODEV; + if (try == 0) + p_dev->io_lines = 16; + + if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0)) + return -EINVAL; + + p_dev->resource[0]->end = 8; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + + return pcmcia_request_io(p_dev); } static int btuart_check_config_notpicky(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, void *priv_data) { static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; int j; - if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { - for (j = 0; j < 5; j++) { - p_dev->resource[0]->start = base[j]; - p_dev->io_lines = base[j] ? 16 : 3; - if (!pcmcia_request_io(p_dev)) - return 0; - } + if (p_dev->io_lines > 3) + return -ENODEV; + + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = 8; + + for (j = 0; j < 5; j++) { + p_dev->resource[0]->start = base[j]; + p_dev->io_lines = base[j] ? 16 : 3; + if (!pcmcia_request_io(p_dev)) + return 0; } return -ENODEV; } diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 4620cc398676..12cd177132fc 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -571,10 +571,7 @@ static int dtl1_probe(struct pcmcia_device *link) info->p_dev = link; link->priv = info; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - link->resource[0]->end = 8; - - link->config_flags |= CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; return dtl1_config(link); } @@ -589,17 +586,14 @@ static void dtl1_detach(struct pcmcia_device *link) kfree(info); } -static int dtl1_confcheck(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int dtl1_confcheck(struct pcmcia_device *p_dev, void *priv_data) { - if ((cf->io.nwin != 1) || (cf->io.win[0].len <= 8)) + if ((p_dev->resource[1]->end) || (p_dev->resource[1]->end < 8)) return -ENODEV; - p_dev->resource[0]->start = cf->io.win[0].base; - p_dev->resource[0]->end = cf->io.win[0].len; /*yo */ - p_dev->io_lines = cf->io.flags & CISTPL_IO_LINES_MASK; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + return pcmcia_request_io(p_dev); } diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 0b2f3b9d261a..79de9ccb8caf 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -1741,19 +1741,8 @@ static void cmm_cm4000_release(struct pcmcia_device * link) /*==== Interface to PCMCIA Layer =======================================*/ -static int cm4000_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int cm4000_config_check(struct pcmcia_device *p_dev, void *priv_data) { - if (!cfg->io.nwin) - return -ENODEV; - - p_dev->resource[0]->start = cfg->io.win[0].base; - p_dev->resource[0]->end = cfg->io.win[0].len; - p_dev->resource[0]->flags |= pcmcia_io_cfg_data_width(cfg->io.flags); - p_dev->io_lines = cfg->io.flags & CISTPL_IO_LINES_MASK; - return pcmcia_request_io(p_dev); } @@ -1761,6 +1750,8 @@ static int cm4000_config(struct pcmcia_device * link, int devno) { struct cm4000_dev *dev; + link->config_flags |= CONF_AUTO_SET_IO; + /* read the config-tuples */ if (pcmcia_loop_config(link, cm4000_config_check, NULL)) goto cs_release; diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index acf88d5c72b2..bf012d228a9e 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -515,25 +515,9 @@ static void cm4040_reader_release(struct pcmcia_device *link) return; } -static int cm4040_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int cm4040_config_check(struct pcmcia_device *p_dev, void *priv_data) { - int rc; - if (!cfg->io.nwin) - return -ENODEV; - - /* Get the IOaddr */ - p_dev->resource[0]->start = cfg->io.win[0].base; - p_dev->resource[0]->end = cfg->io.win[0].len; - p_dev->resource[0]->flags |= pcmcia_io_cfg_data_width(cfg->io.flags); - p_dev->io_lines = cfg->io.flags & CISTPL_IO_LINES_MASK; - rc = pcmcia_request_io(p_dev); - - dev_printk(KERN_INFO, &p_dev->dev, - "pcmcia_request_io returned 0x%x\n", rc); - return rc; + return pcmcia_request_io(p_dev); } @@ -542,6 +526,8 @@ static int reader_config(struct pcmcia_device *link, int devno) struct reader_dev *dev; int fail_rc; + link->config_flags |= CONF_AUTO_SET_IO; + if (pcmcia_loop_config(link, cm4040_config_check, NULL)) goto cs_release; diff --git a/drivers/char/pcmcia/ipwireless/main.c b/drivers/char/pcmcia/ipwireless/main.c index 1b7f0920737b..594c23be69f5 100644 --- a/drivers/char/pcmcia/ipwireless/main.c +++ b/drivers/char/pcmcia/ipwireless/main.c @@ -75,22 +75,18 @@ static void signalled_reboot_callback(void *callback_data) schedule_work(&ipw->work_reboot); } -static int ipwireless_probe(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data) { struct ipw_dev *ipw = priv_data; struct resource *io_resource; int ret; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - p_dev->resource[0]->start = cfg->io.win[0].base; - p_dev->resource[0]->end = cfg->io.win[0].len; /* 0x40 causes it to generate level mode interrupts. */ /* 0x04 enables IREQ pin. */ - p_dev->config_index = cfg->index | 0x44; + p_dev->config_index |= 0x44; p_dev->io_lines = 16; ret = pcmcia_request_io(p_dev); if (ret) @@ -100,26 +96,18 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, resource_size(p_dev->resource[0]), IPWIRELESS_PCCARD_NAME); - if (cfg->mem.nwin == 0) - return 0; - p_dev->resource[2]->flags |= WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM | WIN_ENABLE; - p_dev->resource[2]->start = cfg->mem.win[0].host_addr; - p_dev->resource[2]->end = cfg->mem.win[0].len; - if (p_dev->resource[2]->end < 0x1000) - p_dev->resource[2]->end = 0x1000; ret = pcmcia_request_window(p_dev, p_dev->resource[2], 0); if (ret != 0) goto exit1; - ret = pcmcia_map_mem_page(p_dev, p_dev->resource[2], - cfg->mem.win[0].card_addr); + ret = pcmcia_map_mem_page(p_dev, p_dev->resource[2], p_dev->card_addr); if (ret != 0) goto exit2; - ipw->is_v2_card = cfg->mem.win[0].len == 0x100; + ipw->is_v2_card = resource_size(p_dev->resource[2]) == 0x100; ipw->attr_memory = ioremap(p_dev->resource[2]->start, resource_size(p_dev->resource[2])); @@ -165,13 +153,13 @@ static int config_ipwireless(struct ipw_dev *ipw) int ret = 0; ipw->is_v2_card = 0; + link->config_flags |= CONF_AUTO_SET_IO | CONF_AUTO_SET_IOMEM | + CONF_ENABLE_IRQ; ret = pcmcia_loop_config(link, ipwireless_probe, ipw); if (ret != 0) return ret; - link->config_flags |= CONF_ENABLE_IRQ; - INIT_WORK(&ipw->work_reboot, signalled_reboot_work); ipwireless_init_hardware_v1(ipw->hardware, link->resource[0]->start, diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index c701434f76b7..a343b8f817e4 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -561,19 +561,8 @@ static int mgslpc_probe(struct pcmcia_device *link) /* Card has been inserted. */ -static int mgslpc_ioprobe(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int mgslpc_ioprobe(struct pcmcia_device *p_dev, void *priv_data) { - if (!cfg->io.nwin) - return -ENODEV; - - p_dev->resource[0]->start = cfg->io.win[0].base; - p_dev->resource[0]->end = cfg->io.win[0].len; - p_dev->resource[0]->flags |= pcmcia_io_cfg_data_width(cfg->io.flags); - p_dev->io_lines = cfg->io.flags & CISTPL_IO_LINES_MASK; - return pcmcia_request_io(p_dev); } @@ -585,11 +574,12 @@ static int mgslpc_config(struct pcmcia_device *link) if (debug_level >= DEBUG_LEVEL_INFO) printk("mgslpc_config(0x%p)\n", link); + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + ret = pcmcia_loop_config(link, mgslpc_ioprobe, NULL); if (ret != 0) goto failed; - link->config_flags |= CONF_ENABLE_IRQ; link->config_index = 8; link->config_regs = PRESENT_OPTION; diff --git a/drivers/ide/ide-cs.c b/drivers/ide/ide-cs.c index 25b8a105a98d..c389d9a28881 100644 --- a/drivers/ide/ide-cs.c +++ b/drivers/ide/ide-cs.c @@ -96,10 +96,8 @@ static int ide_probe(struct pcmcia_device *link) info->p_dev = link; link->priv = info; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - link->config_flags |= CONF_ENABLE_IRQ; - link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO | + CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC; return ide_config(link); } /* ide_attach */ @@ -194,52 +192,31 @@ out_release: ======================================================================*/ -struct pcmcia_config_check { - unsigned long ctl_base; - int is_kme; -}; - -static int pcmcia_check_one_config(struct pcmcia_device *pdev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int pcmcia_check_one_config(struct pcmcia_device *pdev, void *priv_data) { - struct pcmcia_config_check *stk = priv_data; - - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - pdev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - pdev->config_index = cfg->index; - pdev->resource[0]->start = io->win[0].base; - if (!(io->flags & CISTPL_IO_16BIT)) { - pdev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - pdev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - } - if (io->nwin == 2) { - pdev->resource[0]->end = 8; - pdev->resource[1]->start = io->win[1].base; - pdev->resource[1]->end = (stk->is_kme) ? 2 : 1; - if (pcmcia_request_io(pdev) != 0) - return -ENODEV; - stk->ctl_base = pdev->resource[1]->start; - } else if ((io->nwin == 1) && (io->win[0].len >= 16)) { - pdev->resource[0]->end = io->win[0].len; - pdev->resource[1]->end = 0; - if (pcmcia_request_io(pdev) != 0) - return -ENODEV; - stk->ctl_base = pdev->resource[0]->start + 0x0e; - } else + int *is_kme = priv_data; + + if (!(pdev->resource[0]->flags & IO_DATA_PATH_WIDTH_8)) { + pdev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + pdev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + } + pdev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; + pdev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; + + if (pdev->resource[1]->end) { + pdev->resource[0]->end = 8; + pdev->resource[1]->end = (*is_kme) ? 2 : 1; + } else { + if (pdev->resource[0]->end < 16) return -ENODEV; - /* If we've got this far, we're done */ - return 0; } - return -ENODEV; + + return pcmcia_request_io(pdev); } static int ide_config(struct pcmcia_device *link) { ide_info_t *info = link->priv; - struct pcmcia_config_check *stk = NULL; int ret = 0, is_kme = 0; unsigned long io_base, ctl_base; struct ide_host *host; @@ -250,19 +227,16 @@ static int ide_config(struct pcmcia_device *link) ((link->card_id == PRODID_KME_KXLC005_A) || (link->card_id == PRODID_KME_KXLC005_B))); - stk = kzalloc(sizeof(*stk), GFP_KERNEL); - if (!stk) - goto err_mem; - stk->is_kme = is_kme; - io_base = ctl_base = 0; - - if (pcmcia_loop_config(link, pcmcia_check_one_config, stk)) { + if (pcmcia_loop_config(link, pcmcia_check_one_config, &is_kme)) { link->config_flags &= ~CONF_AUTO_CHECK_VCC; - if (pcmcia_loop_config(link, pcmcia_check_one_config, stk)) + if (pcmcia_loop_config(link, pcmcia_check_one_config, &is_kme)) goto failed; /* No suitable config found */ } io_base = link->resource[0]->start; - ctl_base = stk->ctl_base; + if (link->resource[1]->end) + ctl_base = link->resource[1]->start; + else + ctl_base = link->resource[0]->start + 0x0e; if (!link->irq) goto failed; @@ -294,15 +268,9 @@ static int ide_config(struct pcmcia_device *link) 'a' + host->ports[0]->index * 2, link->vpp / 10, link->vpp % 10); - kfree(stk); return 0; -err_mem: - printk(KERN_NOTICE "ide-cs: ide_config failed memory allocation\n"); - goto failed; - failed: - kfree(stk); ide_release(link); return -ENODEV; } /* ide_config */ diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c index 9dbab9c99bea..403a995bec95 100644 --- a/drivers/isdn/hardware/avm/avm_cs.c +++ b/drivers/isdn/hardware/avm/avm_cs.c @@ -72,13 +72,8 @@ static void avmcs_detach(struct pcmcia_device *p_dev); static int avmcs_probe(struct pcmcia_device *p_dev) { - - /* The io structure describes IO port mapping */ - p_dev->resource[0]->end = 16; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - /* General socket configuration */ - p_dev->config_flags |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; p_dev->config_index = 1; p_dev->config_regs = PRESENT_OPTION; @@ -107,16 +102,12 @@ static void avmcs_detach(struct pcmcia_device *link) ======================================================================*/ -static int avmcs_configcheck(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int avmcs_configcheck(struct pcmcia_device *p_dev, void *priv_data) { - if (cf->io.nwin <= 0) - return -ENODEV; + p_dev->resource[0]->end = 16; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - p_dev->resource[0]->start = cf->io.win[0].base; - p_dev->resource[0]->end = cf->io.win[0].len; return pcmcia_request_io(p_dev); } diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c index 2f2b0005f07b..cb09f0cacd12 100644 --- a/drivers/isdn/hisax/avma1_cs.c +++ b/drivers/isdn/hisax/avma1_cs.c @@ -76,14 +76,8 @@ static int __devinit avma1cs_probe(struct pcmcia_device *p_dev) { dev_dbg(&p_dev->dev, "avma1cs_attach()\n"); - /* The io structure describes IO port mapping */ - p_dev->resource[0]->end = 16; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - p_dev->resource[1]->end = 16; - p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_16; - /* General socket configuration */ - p_dev->config_flags |= CONF_ENABLE_IRQ; + p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; p_dev->config_index = 1; p_dev->config_regs = PRESENT_OPTION; @@ -114,17 +108,13 @@ static void __devexit avma1cs_detach(struct pcmcia_device *link) ======================================================================*/ -static int avma1cs_configcheck(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int avma1cs_configcheck(struct pcmcia_device *p_dev, void *priv_data) { - if (cf->io.nwin <= 0) - return -ENODEV; - - p_dev->resource[0]->start = cf->io.win[0].base; - p_dev->resource[0]->end = cf->io.win[0].len; + p_dev->resource[0]->end = 16; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; p_dev->io_lines = 5; + return pcmcia_request_io(p_dev); } diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c index 0a65280be8d5..f203a52aab2a 100644 --- a/drivers/isdn/hisax/elsa_cs.c +++ b/drivers/isdn/hisax/elsa_cs.c @@ -118,16 +118,6 @@ static int __devinit elsa_cs_probe(struct pcmcia_device *link) local->cardnr = -1; - /* - General socket configuration defaults can go here. In this - client, we assume very little, and rely on the CIS for almost - everything. In most clients, many details (i.e., number, sizes, - and attributes of IO windows) are fixed by the nature of the - device, and can be hard-wired here. - */ - link->resource[0]->end = 8; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - return elsa_cs_config(link); } /* elsa_cs_attach */ @@ -160,18 +150,17 @@ static void __devexit elsa_cs_detach(struct pcmcia_device *link) ======================================================================*/ -static int elsa_cs_configcheck(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int elsa_cs_configcheck(struct pcmcia_device *p_dev, void *priv_data) { int j; p_dev->io_lines = 3; + p_dev->resource[0]->end = 8; + p_dev->resource[0]->flags &= IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - if ((cf->io.nwin > 0) && cf->io.win[0].base) { + if ((p_dev->resource[0]->end) && p_dev->resource[0]->start) { printk(KERN_INFO "(elsa_cs: looks like the 96 model)\n"); - p_dev->resource[0]->start = cf->io.win[0].base; if (!pcmcia_request_io(p_dev)) return 0; } else { @@ -194,6 +183,8 @@ static int __devinit elsa_cs_config(struct pcmcia_device *link) dev_dbg(&link->dev, "elsa_config(0x%p)\n", link); dev = link->priv; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + i = pcmcia_loop_config(link, elsa_cs_configcheck, NULL); if (i != 0) goto failed; diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c index b69eccfdbb05..a88c88f6cdeb 100644 --- a/drivers/isdn/hisax/sedlbauer_cs.c +++ b/drivers/isdn/hisax/sedlbauer_cs.c @@ -128,8 +128,6 @@ static int __devinit sedlbauer_probe(struct pcmcia_device *link) /* from old sedl_cs */ /* The io structure describes IO port mapping */ - link->resource[0]->end = 8; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; return sedlbauer_config(link); } /* sedlbauer_attach */ @@ -161,35 +159,13 @@ static void __devexit sedlbauer_detach(struct pcmcia_device *link) device available to the system. ======================================================================*/ -static int sedlbauer_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int sedlbauer_config_check(struct pcmcia_device *p_dev, void *priv_data) { - if (cfg->index == 0) - return -ENODEV; - - /* IO window settings */ - p_dev->resource[0]->end = p_dev->resource[1]->end = 0; - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= - pcmcia_io_cfg_data_width(io->flags); - if (io->nwin > 1) { - p_dev->resource[1]->flags = p_dev->resource[0]->flags; - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - /* This reserves IO space but doesn't actually enable it */ - p_dev->io_lines = 3; - if (pcmcia_request_io(p_dev) != 0) - return -ENODEV; - } + if (p_dev->config_index == 0) + return -EINVAL; - return 0; + p_dev->io_lines = 3; + return pcmcia_request_io(p_dev); } @@ -202,7 +178,7 @@ static int __devinit sedlbauer_config(struct pcmcia_device *link) dev_dbg(&link->dev, "sedlbauer_config(0x%p)\n", link); link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_CHECK_VCC | - CONF_AUTO_SET_VPP | CONF_AUTO_AUDIO; + CONF_AUTO_SET_VPP | CONF_AUTO_AUDIO | CONF_AUTO_SET_IO; /* In this loop, we scan the CIS for configuration table entries, diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c index 6605480aa37f..05a5631963bb 100644 --- a/drivers/isdn/hisax/teles_cs.c +++ b/drivers/isdn/hisax/teles_cs.c @@ -105,10 +105,7 @@ static int __devinit teles_probe(struct pcmcia_device *link) and attributes of IO windows) are fixed by the nature of the device, and can be hard-wired here. */ - link->resource[0]->end = 96; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - - link->config_flags |= CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; return teles_cs_config(link); } /* teles_attach */ @@ -142,18 +139,17 @@ static void __devexit teles_detach(struct pcmcia_device *link) ======================================================================*/ -static int teles_cs_configcheck(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int teles_cs_configcheck(struct pcmcia_device *p_dev, void *priv_data) { int j; p_dev->io_lines = 5; + p_dev->resource[0]->end = 96; + p_dev->resource[0]->flags &= IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - if ((cf->io.nwin > 0) && cf->io.win[0].base) { + if ((p_dev->resource[0]->end) && p_dev->resource[0]->start) { printk(KERN_INFO "(teles_cs: looks like the 96 model)\n"); - p_dev->resource[0]->start = cf->io.win[0].base; if (!pcmcia_request_io(p_dev)) return 0; } else { diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c index 17f1040e255e..9d9d997f2e59 100644 --- a/drivers/net/pcmcia/axnet_cs.c +++ b/drivers/net/pcmcia/axnet_cs.c @@ -284,34 +284,16 @@ static int try_io_port(struct pcmcia_device *link) } } -static int axnet_configcheck(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int axnet_configcheck(struct pcmcia_device *p_dev, void *priv_data) { - int i; - cistpl_io_t *io = &cfg->io; - - if (cfg->index == 0 || cfg->io.nwin == 0) - return -ENODEV; + if (p_dev->config_index == 0) + return -EINVAL; p_dev->config_index = 0x05; - /* For multifunction cards, by convention, we configure the - network function with window 0, and serial with window 1 */ - if (io->nwin > 1) { - i = (io->win[1].len > io->win[0].len); - p_dev->resource[1]->start = io->win[1-i].base; - p_dev->resource[1]->end = io->win[1-i].len; - } else { - i = p_dev->resource[1]->end = 0; - } - p_dev->resource[0]->start = io->win[i].base; - p_dev->resource[0]->end = io->win[i].len; - p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - if (p_dev->resource[0]->end + p_dev->resource[1]->end >= 32) - return try_io_port(p_dev); + if (p_dev->resource[0]->end + p_dev->resource[1]->end < 32) + return -ENODEV; - return -ENODEV; + return try_io_port(p_dev); } static int axnet_config(struct pcmcia_device *link) @@ -324,6 +306,7 @@ static int axnet_config(struct pcmcia_device *link) /* don't trust the CIS on this; Linksys got it wrong */ link->config_regs = 0x63; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; ret = pcmcia_loop_config(link, axnet_configcheck, NULL); if (ret != 0) goto failed; @@ -331,7 +314,6 @@ static int axnet_config(struct pcmcia_device *link) if (!link->irq) goto failed; - link->config_flags |= CONF_ENABLE_IRQ; if (resource_size(link->resource[1]) == 8) link->config_flags |= CONF_ENABLE_SPKR; diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c index c1479e3bfab4..792ab38d979c 100644 --- a/drivers/net/pcmcia/fmvj18x_cs.c +++ b/drivers/net/pcmcia/fmvj18x_cs.c @@ -319,10 +319,7 @@ static int ungermann_try_io_port(struct pcmcia_device *link) return ret; /* RequestIO failed */ } -static int fmvj18x_ioprobe(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int fmvj18x_ioprobe(struct pcmcia_device *p_dev, void *priv_data) { return 0; /* strange, but that's what the code did already before... */ } diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index 12b028c6abc9..ffe2587b9145 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -259,7 +259,7 @@ static int pcnet_probe(struct pcmcia_device *link) info->p_dev = link; link->priv = dev; - link->config_flags |= CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; dev->netdev_ops = &pcnet_netdev_ops; @@ -500,42 +500,22 @@ static int try_io_port(struct pcmcia_device *link) } } -static int pcnet_confcheck(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int pcnet_confcheck(struct pcmcia_device *p_dev, void *priv_data) { int *priv = priv_data; int try = (*priv & 0x1); - int i; - cistpl_io_t *io = &cfg->io; - if (cfg->index == 0 || cfg->io.nwin == 0) - return -EINVAL; + *priv &= (p_dev->resource[2]->end >= 0x4000) ? 0x10 : ~0x10; - /* For multifunction cards, by convention, we configure the - network function with window 0, and serial with window 1 */ - if (io->nwin > 1) { - i = (io->win[1].len > io->win[0].len); - p_dev->resource[1]->start = io->win[1-i].base; - p_dev->resource[1]->end = io->win[1-i].len; - } else { - i = p_dev->resource[1]->end = 0; - } + if (p_dev->config_index == 0) + return -EINVAL; - *priv &= ((cfg->mem.nwin == 1) && - (cfg->mem.win[0].len >= 0x4000)) ? 0x10 : ~0x10; + if (p_dev->resource[0]->end + p_dev->resource[1]->end < 32) + return -EINVAL; - p_dev->resource[0]->start = io->win[i].base; - p_dev->resource[0]->end = io->win[i].len; - if (!try) - p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - else + if (try) p_dev->io_lines = 16; - if (p_dev->resource[0]->end + p_dev->resource[1]->end >= 32) - return try_io_port(p_dev); - - return -EINVAL; + return try_io_port(p_dev); } static hw_info_t *pcnet_try_config(struct pcmcia_device *link, diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c index e127d2b546dd..a8cef28507de 100644 --- a/drivers/net/pcmcia/smc91c92_cs.c +++ b/drivers/net/pcmcia/smc91c92_cs.c @@ -323,9 +323,6 @@ static int smc91c92_probe(struct pcmcia_device *link) link->priv = dev; spin_lock_init(&smc->lock); - link->resource[0]->end = 16; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->config_flags |= CONF_ENABLE_IRQ; /* The SMC91c92-specific entries in the device structure. */ dev->netdev_ops = &smc_netdev_ops; @@ -417,18 +414,21 @@ static int mhz_3288_power(struct pcmcia_device *link) return 0; } -static int mhz_mfc_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int mhz_mfc_config_check(struct pcmcia_device *p_dev, void *priv_data) { int k; - p_dev->resource[1]->start = cf->io.win[0].base; + p_dev->io_lines = 16; + p_dev->resource[1]->start = p_dev->resource[0]->start; + p_dev->resource[1]->end = 8; + p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = 16; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; for (k = 0; k < 0x400; k += 0x10) { if (k & 0x80) continue; p_dev->resource[0]->start = k ^ 0x300; - p_dev->io_lines = 16; if (!pcmcia_request_io(p_dev)) return 0; } @@ -442,9 +442,8 @@ static int mhz_mfc_config(struct pcmcia_device *link) unsigned int offset; int i; - link->config_flags |= CONF_ENABLE_SPKR; - link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - link->resource[1]->end = 8; + link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ | + CONF_AUTO_SET_IO; /* The Megahertz combo cards have modem-like CIS entries, so we have to explicitly try a bunch of port combinations. */ @@ -586,13 +585,12 @@ static int mot_setup(struct pcmcia_device *link) /*====================================================================*/ -static int smc_configcheck(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int smc_configcheck(struct pcmcia_device *p_dev, void *priv_data) { - p_dev->resource[0]->start = cf->io.win[0].base; - p_dev->io_lines = cf->io.flags & CISTPL_IO_LINES_MASK; + p_dev->resource[0]->end = 16; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + return pcmcia_request_io(p_dev); } @@ -601,7 +599,8 @@ static int smc_config(struct pcmcia_device *link) struct net_device *dev = link->priv; int i; - link->resource[0]->end = 16; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + i = pcmcia_loop_config(link, smc_configcheck, NULL); if (!i) dev->base_addr = link->resource[0]->start; @@ -634,7 +633,7 @@ static int osi_config(struct pcmcia_device *link) static const unsigned int com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; int i, j; - link->config_flags |= CONF_ENABLE_SPKR; + link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ; link->resource[0]->end = 64; link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; link->resource[1]->end = 8; diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c index 2bc2eb89c4cd..cecc07454e9e 100644 --- a/drivers/net/pcmcia/xirc2ps_cs.c +++ b/drivers/net/pcmcia/xirc2ps_cs.c @@ -528,7 +528,6 @@ xirc2ps_probe(struct pcmcia_device *link) link->priv = dev; /* General socket configuration */ - link->config_flags |= CONF_ENABLE_IRQ; link->config_index = 1; /* Fill in card specific entries */ @@ -665,42 +664,53 @@ has_ce2_string(struct pcmcia_device * p_dev) } static int -xirc2ps_config_modem(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - void *priv_data) +xirc2ps_config_modem(struct pcmcia_device *p_dev, void *priv_data) { unsigned int ioaddr; - if (cf->io.nwin > 0 && (cf->io.win[0].base & 0xf) == 8) { - for (ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) { - p_dev->resource[1]->start = cf->io.win[0].base; - p_dev->resource[0]->start = ioaddr; - if (!pcmcia_request_io(p_dev)) - return 0; - } + if ((p_dev->resource[0]->start & 0xf) == 8) + return -ENODEV; + + p_dev->resource[0]->end = 16; + p_dev->resource[1]->end = 8; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; + p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->io_lines = 10; + + p_dev->resource[1]->start = p_dev->resource[0]->start; + for (ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) { + p_dev->resource[0]->start = ioaddr; + if (!pcmcia_request_io(p_dev)) + return 0; } return -ENODEV; } static int -xirc2ps_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - void *priv_data) +xirc2ps_config_check(struct pcmcia_device *p_dev, void *priv_data) { int *pass = priv_data; + resource_size_t tmp = p_dev->resource[1]->start; - if (cf->io.nwin > 0 && (cf->io.win[0].base & 0xf) == 8) { - p_dev->resource[1]->start = cf->io.win[0].base; - p_dev->resource[0]->start = p_dev->resource[1]->start - + (*pass ? (cf->index & 0x20 ? -24:8) - : (cf->index & 0x20 ? 8:-24)); - if (!pcmcia_request_io(p_dev)) - return 0; - } - return -ENODEV; + tmp += (*pass ? (p_dev->config_index & 0x20 ? -24 : 8) + : (p_dev->config_index & 0x20 ? 8 : -24)); + + if ((p_dev->resource[0]->start & 0xf) == 8) + return -ENODEV; + + p_dev->resource[0]->end = 18; + p_dev->resource[1]->end = 8; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; + p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->io_lines = 10; + p_dev->resource[1]->start = p_dev->resource[0]->start; + p_dev->resource[0]->start = tmp; + return pcmcia_request_io(p_dev); } @@ -803,21 +813,16 @@ xirc2ps_config(struct pcmcia_device * link) goto failure; } - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; - link->io_lines = 10; if (local->modem) { int pass; + link->config_flags |= CONF_AUTO_SET_IO; - link->resource[1]->end = 8; - link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; if (local->dingo) { /* Take the Modem IO port from the CIS and scan for a free * Ethernet port */ - link->resource[0]->end = 16; /* no Mako stuff anymore */ if (!pcmcia_loop_config(link, xirc2ps_config_modem, NULL)) goto port_found; } else { - link->resource[0]->end = 18; /* We do 2 passes here: The first one uses the regular mapping and * the second tries again, thereby considering that the 32 ports are * mirrored every 32 bytes. Actually we use a mirrored port for @@ -833,7 +838,9 @@ xirc2ps_config(struct pcmcia_device * link) } printk(KNOT_XIRC "no ports available\n"); } else { + link->io_lines = 10; link->resource[0]->end = 16; + link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; for (ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) { link->resource[0]->start = ioaddr; if (!(err = pcmcia_request_io(link))) diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/airo_cs.c index 63bf662e9c7f..77682f27772b 100644 --- a/drivers/net/wireless/airo_cs.c +++ b/drivers/net/wireless/airo_cs.c @@ -137,36 +137,12 @@ static void airo_detach(struct pcmcia_device *link) ======================================================================*/ -static int airo_cs_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int airo_cs_config_check(struct pcmcia_device *p_dev, void *priv_data) { - if (cfg->index == 0) - return -ENODEV; - - /* IO window settings */ - p_dev->resource[0]->end = p_dev->resource[1]->end = 0; - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= - pcmcia_io_cfg_data_width(io->flags); - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - if (io->nwin > 1) { - p_dev->resource[1]->flags = p_dev->resource[0]->flags; - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - } - - /* This reserves IO space but doesn't actually enable it */ - if (pcmcia_request_io(p_dev) != 0) - return -ENODEV; + if (p_dev->config_index == 0) + return -EINVAL; - /* If we got this far, we're cool! */ - return 0; + return pcmcia_request_io(p_dev); } @@ -180,7 +156,7 @@ static int airo_config(struct pcmcia_device *link) dev_dbg(&link->dev, "airo_config\n"); link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | - CONF_AUTO_AUDIO; + CONF_AUTO_AUDIO | CONF_AUTO_SET_IO; /* * In this loop, we scan the CIS for configuration table diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c index 812decd3fbe9..202938022112 100644 --- a/drivers/net/wireless/atmel_cs.c +++ b/drivers/net/wireless/atmel_cs.c @@ -154,31 +154,11 @@ static int card_present(void *arg) return 0; } -static int atmel_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int atmel_config_check(struct pcmcia_device *p_dev, void *priv_data) { - if (cfg->index == 0) - return -ENODEV; - - /* IO window settings */ - p_dev->resource[0]->end = p_dev->resource[1]->end = 0; - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= - pcmcia_io_cfg_data_width(io->flags); - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - if (io->nwin > 1) { - p_dev->resource[1]->flags = p_dev->resource[0]->flags; - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - } + if (p_dev->config_index == 0) + return -EINVAL; - /* This reserves IO space but doesn't actually enable it */ return pcmcia_request_io(p_dev); } @@ -194,7 +174,7 @@ static int atmel_config(struct pcmcia_device *link) dev_dbg(&link->dev, "atmel_config\n"); link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | - CONF_AUTO_AUDIO; + CONF_AUTO_AUDIO | CONF_AUTO_SET_IO; /* In this loop, we scan the CIS for configuration table entries, diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index d4f19af1757f..e57b20134d39 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c @@ -469,46 +469,11 @@ static void prism2_detach(struct pcmcia_device *link) /* run after a CARD_INSERTION event is received to configure the PCMCIA * socket and make the device available to the system */ -static int prism2_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int prism2_config_check(struct pcmcia_device *p_dev, void *priv_data) { - if (cfg->index == 0) - return -ENODEV; - - PDEBUG(DEBUG_EXTRA, "Checking CFTABLE_ENTRY 0x%02X " - "(default 0x%02X)\n", cfg->index, dflt->index); - - if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) - p_dev->vpp = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; - else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM)) - p_dev->vpp = dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000; - - /* Do we need to allocate an interrupt? */ - p_dev->config_flags |= CONF_ENABLE_IRQ; - - /* IO window settings */ - PDEBUG(DEBUG_EXTRA, "IO window settings: cfg->io.nwin=%d " - "dflt->io.nwin=%d\n", - cfg->io.nwin, dflt->io.nwin); - p_dev->resource[0]->end = p_dev->resource[1]->end = 0; - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= - pcmcia_io_cfg_data_width(io->flags); - p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - if (io->nwin > 1) { - p_dev->resource[1]->flags = p_dev->resource[0]->flags; - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - } + if (p_dev->config_index == 0) + return -EINVAL; - /* This reserves IO space but doesn't actually enable it */ return pcmcia_request_io(p_dev); } @@ -531,7 +496,7 @@ static int prism2_config(struct pcmcia_device *link) /* Look for an appropriate configuration table entry in the CIS */ link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_AUDIO | - CONF_AUTO_CHECK_VCC; + CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; if (ignore_cis_vcc) link->config_flags &= ~CONF_AUTO_CHECK_VCC; ret = pcmcia_loop_config(link, prism2_config_check, NULL); diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c index 031f3e6da3c9..2c6f28ac5197 100644 --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c @@ -794,20 +794,12 @@ static void if_cs_release(struct pcmcia_device *p_dev) * insertion event. */ -static int if_cs_ioprobe(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int if_cs_ioprobe(struct pcmcia_device *p_dev, void *priv_data) { + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - p_dev->resource[0]->start = cfg->io.win[0].base; - p_dev->resource[0]->end = cfg->io.win[0].len; - /* Do we need to allocate an interrupt? */ - p_dev->config_flags |= CONF_ENABLE_IRQ; - - /* IO window settings */ - if (cfg->io.nwin != 1) { + if (p_dev->resource[1]->end) { lbs_pr_err("wrong CIS (check number of IO windows)\n"); return -ENODEV; } @@ -833,6 +825,8 @@ static int if_cs_probe(struct pcmcia_device *p_dev) card->p_dev = p_dev; p_dev->priv = card; + p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + if (pcmcia_loop_config(p_dev, if_cs_ioprobe, NULL)) { lbs_pr_err("error in pcmcia_loop_config\n"); goto out1; diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c index b92173827b55..263dfe9e0e30 100644 --- a/drivers/net/wireless/orinoco/orinoco_cs.c +++ b/drivers/net/wireless/orinoco/orinoco_cs.c @@ -142,42 +142,12 @@ static void orinoco_cs_detach(struct pcmcia_device *link) * device available to the system. */ -static int orinoco_cs_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int orinoco_cs_config_check(struct pcmcia_device *p_dev, void *priv_data) { - if (cfg->index == 0) - goto next_entry; - - /* Do we need to allocate an interrupt? */ - p_dev->config_flags |= CONF_ENABLE_IRQ; - - /* IO window settings */ - p_dev->resource[0]->end = p_dev->resource[1]->end = 0; - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= - pcmcia_io_cfg_data_width(io->flags); - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - if (io->nwin > 1) { - p_dev->resource[1]->flags = p_dev->resource[0]->flags; - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - - /* This reserves IO space but doesn't actually enable it */ - if (pcmcia_request_io(p_dev) != 0) - goto next_entry; - } - return 0; + if (p_dev->config_index == 0) + return -EINVAL; -next_entry: - pcmcia_disable_device(p_dev); - return -ENODEV; + return pcmcia_request_io(p_dev); }; static int @@ -202,7 +172,8 @@ orinoco_cs_config(struct pcmcia_device *link) * and most client drivers will only use the CIS to fill in * implementation-defined details. */ - link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC; + link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC | + CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; if (ignore_cis_vcc) link->config_flags &= ~CONF_AUTO_CHECK_VCC; ret = pcmcia_loop_config(link, orinoco_cs_config_check, NULL); diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c index f462c78856e9..78446507873f 100644 --- a/drivers/net/wireless/orinoco/spectrum_cs.c +++ b/drivers/net/wireless/orinoco/spectrum_cs.c @@ -205,48 +205,12 @@ static void spectrum_cs_detach(struct pcmcia_device *link) */ static int spectrum_cs_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, void *priv_data) { - if (cfg->index == 0) - goto next_entry; - - if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) - p_dev->vpp = - cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; - else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM)) - p_dev->vpp = - dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000; - - /* Do we need to allocate an interrupt? */ - p_dev->config_flags |= CONF_ENABLE_IRQ; - - /* IO window settings */ - p_dev->resource[0]->end = p_dev->resource[1]->end = 0; - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= - pcmcia_io_cfg_data_width(io->flags); - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - if (io->nwin > 1) { - p_dev->resource[1]->flags = p_dev->resource[0]->flags; - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - - /* This reserves IO space but doesn't actually enable it */ - if (pcmcia_request_io(p_dev) != 0) - goto next_entry; - } - return 0; + if (p_dev->config_index == 0) + return -EINVAL; -next_entry: - pcmcia_disable_device(p_dev); - return -ENODEV; + return pcmcia_request_io(p_dev); }; static int @@ -271,7 +235,8 @@ spectrum_cs_config(struct pcmcia_device *link) * and most client drivers will only use the CIS to fill in * implementation-defined details. */ - link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC; + link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC | + CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; if (ignore_cis_vcc) link->config_flags &= ~CONF_AUTO_CHECK_VCC; ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL); diff --git a/drivers/parport/parport_cs.c b/drivers/parport/parport_cs.c index 8c2a4733bc19..3730184a04a3 100644 --- a/drivers/parport/parport_cs.c +++ b/drivers/parport/parport_cs.c @@ -100,9 +100,7 @@ static int parport_probe(struct pcmcia_device *link) link->priv = info; info->p_dev = link; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - link->config_flags |= CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; return parport_config(link); } /* parport_attach */ @@ -133,27 +131,14 @@ static void parport_detach(struct pcmcia_device *link) ======================================================================*/ -static int parport_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int parport_config_check(struct pcmcia_device *p_dev, void *priv_data) { - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - if (epp_mode) - p_dev->config_index |= FORCE_EPP_MODE; - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - if (io->nwin == 2) { - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - if (pcmcia_request_io(p_dev) != 0) - return -ENODEV; - return 0; - } - return -ENODEV; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; + + return pcmcia_request_io(p_dev); } static int parport_config(struct pcmcia_device *link) @@ -164,6 +149,9 @@ static int parport_config(struct pcmcia_device *link) dev_dbg(&link->dev, "parport_config\n"); + if (epp_mode) + link->config_index |= FORCE_EPP_MODE; + ret = pcmcia_loop_config(link, parport_config_check, NULL); if (ret) goto failed; diff --git a/drivers/pcmcia/pcmcia_cis.c b/drivers/pcmcia/pcmcia_cis.c index 160da0697335..e2c92415b892 100644 --- a/drivers/pcmcia/pcmcia_cis.c +++ b/drivers/pcmcia/pcmcia_cis.c @@ -6,7 +6,7 @@ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. * * Copyright (C) 1999 David A. Hinds - * Copyright (C) 2004-2009 Dominik Brodowski + * Copyright (C) 2004-2010 Dominik Brodowski * * 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 @@ -125,13 +125,24 @@ next_entry: return ret; } + +/** + * pcmcia_io_cfg_data_width() - convert cfgtable to data path width parameter + */ +static int pcmcia_io_cfg_data_width(unsigned int flags) +{ + if (!(flags & CISTPL_IO_8BIT)) + return IO_DATA_PATH_WIDTH_16; + if (!(flags & CISTPL_IO_16BIT)) + return IO_DATA_PATH_WIDTH_8; + return IO_DATA_PATH_WIDTH_AUTO; +} + + struct pcmcia_cfg_mem { struct pcmcia_device *p_dev; + int (*conf_check) (struct pcmcia_device *p_dev, void *priv_data); void *priv_data; - int (*conf_check) (struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data); cisparse_t parse; cistpl_cftable_entry_t dflt; }; @@ -184,16 +195,63 @@ static int pcmcia_do_loop_config(tuple_t *tuple, cisparse_t *parse, void *priv) if ((flags & CONF_AUTO_AUDIO) && (cfg->flags & CISTPL_CFTABLE_AUDIO)) p_dev->config_flags |= CONF_ENABLE_SPKR; - return cfg_mem->conf_check(cfg_mem->p_dev, cfg, &cfg_mem->dflt, - cfg_mem->priv_data); + + /* IO window settings? */ + if (flags & CONF_AUTO_SET_IO) { + cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; + int i = 0; + + p_dev->resource[0]->start = p_dev->resource[0]->end = 0; + p_dev->resource[1]->start = p_dev->resource[1]->end = 0; + if (io->nwin == 0) + return -ENODEV; + + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= + pcmcia_io_cfg_data_width(io->flags); + if (io->nwin > 1) { + /* For multifunction cards, by convention, we + * configure the network function with window 0, + * and serial with window 1 */ + i = (io->win[1].len > io->win[0].len); + p_dev->resource[1]->flags = p_dev->resource[0]->flags; + p_dev->resource[1]->start = io->win[1-i].base; + p_dev->resource[1]->end = io->win[1-i].len; + } + p_dev->resource[0]->start = io->win[i].base; + p_dev->resource[0]->end = io->win[i].len; + p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; + } + + /* MEM window settings? */ + if (flags & CONF_AUTO_SET_IOMEM) { + /* so far, we only set one memory window */ + cistpl_mem_t *mem = (cfg->mem.nwin) ? &cfg->mem : &dflt->mem; + + p_dev->resource[2]->start = p_dev->resource[2]->end = 0; + if (mem->nwin == 0) + return -ENODEV; + + p_dev->resource[2]->start = mem->win[0].host_addr; + p_dev->resource[2]->end = mem->win[0].len; + if (p_dev->resource[2]->end < 0x1000) + p_dev->resource[2]->end = 0x1000; + p_dev->card_addr = mem->win[0].card_addr; + } + + dev_dbg(&p_dev->dev, + "checking configuration %x: %pr %pr %pr (%d lines)\n", + p_dev->config_index, p_dev->resource[0], p_dev->resource[1], + p_dev->resource[2], p_dev->io_lines); + + return cfg_mem->conf_check(p_dev, cfg_mem->priv_data); } /** * pcmcia_loop_config() - loop over configuration options * @p_dev: the struct pcmcia_device which we need to loop for. * @conf_check: function to call for each configuration option. - * It gets passed the struct pcmcia_device, the CIS data - * describing the configuration option, and private data + * It gets passed the struct pcmcia_device and private data * being passed to pcmcia_loop_config() * @priv_data: private data to be passed to the conf_check function. * @@ -203,8 +261,6 @@ static int pcmcia_do_loop_config(tuple_t *tuple, cisparse_t *parse, void *priv) */ int pcmcia_loop_config(struct pcmcia_device *p_dev, int (*conf_check) (struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, void *priv_data), void *priv_data) { diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c index 0b5fc2fa0589..bd9ce09b7ff8 100644 --- a/drivers/scsi/pcmcia/aha152x_stub.c +++ b/drivers/scsi/pcmcia/aha152x_stub.c @@ -99,9 +99,7 @@ static int aha152x_probe(struct pcmcia_device *link) info->p_dev = link; link->priv = info; - link->resource[0]->end = 0x20; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->config_flags |= CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; link->config_regs = PRESENT_OPTION; return aha152x_config_cs(link); @@ -121,24 +119,24 @@ static void aha152x_detach(struct pcmcia_device *link) /*====================================================================*/ -static int aha152x_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int aha152x_config_check(struct pcmcia_device *p_dev, void *priv_data) { p_dev->io_lines = 10; + /* For New Media T&J, look for a SCSI window */ - if (cfg->io.win[0].len >= 0x20) - p_dev->resource[0]->start = cfg->io.win[0].base; - else if ((cfg->io.nwin > 1) && - (cfg->io.win[1].len >= 0x20)) - p_dev->resource[0]->start = cfg->io.win[1].base; - if ((cfg->io.nwin > 0) && - (p_dev->resource[0]->start < 0xffff)) { - if (!pcmcia_request_io(p_dev)) - return 0; - } - return -EINVAL; + if ((p_dev->resource[0]->end < 0x20) && + (p_dev->resource[1]->end >= 0x20)) + p_dev->resource[0]->start = p_dev->resource[1]->start; + + if (p_dev->resource[0]->start >= 0xffff) + return -EINVAL; + + p_dev->resource[1]->start = p_dev->resource[1]->end = 0; + p_dev->resource[0]->end = 0x20; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + + return pcmcia_request_io(p_dev); } static int aha152x_config_cs(struct pcmcia_device *link) diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c index 3b9f311cb035..f2dc627e9da2 100644 --- a/drivers/scsi/pcmcia/fdomain_stub.c +++ b/drivers/scsi/pcmcia/fdomain_stub.c @@ -82,9 +82,7 @@ static int fdomain_probe(struct pcmcia_device *link) info->p_dev = link; link->priv = info; - link->resource[0]->end = 0x10; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->config_flags |= CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; link->config_regs = PRESENT_OPTION; return fdomain_config(link); @@ -103,13 +101,12 @@ static void fdomain_detach(struct pcmcia_device *link) /*====================================================================*/ -static int fdomain_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int fdomain_config_check(struct pcmcia_device *p_dev, void *priv_data) { p_dev->io_lines = 10; - p_dev->resource[0]->start = cfg->io.win[0].base; + p_dev->resource[0]->end = 0x10; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; return pcmcia_request_io(p_dev); } diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c index 344d49900b4c..3b90ad9d1956 100644 --- a/drivers/scsi/pcmcia/nsp_cs.c +++ b/drivers/scsi/pcmcia/nsp_cs.c @@ -1556,13 +1556,6 @@ static int nsp_cs_probe(struct pcmcia_device *link) nsp_dbg(NSP_DEBUG_INIT, "info=0x%p", info); - /* The io structure describes IO port mapping */ - link->resource[0]->end = 0x10; - link->resource[0]->flags = IO_DATA_PATH_WIDTH_AUTO; - - /* General socket configuration */ - link->config_flags |= CONF_ENABLE_IRQ; - ret = nsp_cs_config(link); nsp_dbg(NSP_DEBUG_INIT, "link=0x%p", link); @@ -1594,50 +1587,27 @@ static void nsp_cs_detach(struct pcmcia_device *link) ethernet device available to the system. ======================================================================*/ -static int nsp_cs_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int nsp_cs_config_check(struct pcmcia_device *p_dev, void *priv_data) { nsp_hw_data *data = priv_data; - if (cfg->index == 0) + if (p_dev->config_index == 0) return -ENODEV; - /* IO window settings */ - p_dev->resource[0]->end = p_dev->resource[1]->end = 0; - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= - pcmcia_io_cfg_data_width(io->flags); - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - if (io->nwin > 1) { - p_dev->resource[1]->flags = p_dev->resource[0]->flags; - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - /* This reserves IO space but doesn't actually enable it */ - if (pcmcia_request_io(p_dev) != 0) - goto next_entry; - } + /* This reserves IO space but doesn't actually enable it */ + if (pcmcia_request_io(p_dev) != 0) + goto next_entry; - if ((cfg->mem.nwin > 0) || (dflt->mem.nwin > 0)) { - cistpl_mem_t *mem = - (cfg->mem.nwin) ? &cfg->mem : &dflt->mem; + if (resource_size(p_dev->resource[2])) { p_dev->resource[2]->flags |= (WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM | WIN_ENABLE); - p_dev->resource[2]->start = mem->win[0].host_addr; - p_dev->resource[2]->end = mem->win[0].len; if (p_dev->resource[2]->end < 0x1000) p_dev->resource[2]->end = 0x1000; if (pcmcia_request_window(p_dev, p_dev->resource[2], 0) != 0) goto next_entry; if (pcmcia_map_mem_page(p_dev, p_dev->resource[2], - mem->win[0].card_addr) != 0) + p_dev->card_addr) != 0) goto next_entry; data->MmioAddress = (unsigned long) @@ -1664,7 +1634,8 @@ static int nsp_cs_config(struct pcmcia_device *link) nsp_dbg(NSP_DEBUG_INIT, "in"); link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_CHECK_VCC | - CONF_AUTO_SET_VPP | CONF_AUTO_AUDIO; + CONF_AUTO_SET_VPP | CONF_AUTO_AUDIO | CONF_AUTO_SET_IOMEM | + CONF_AUTO_SET_IO; ret = pcmcia_loop_config(link, nsp_cs_config_check, data); if (ret) diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c index 468fd12fe98d..e8a06e3a384c 100644 --- a/drivers/scsi/pcmcia/qlogic_stub.c +++ b/drivers/scsi/pcmcia/qlogic_stub.c @@ -155,9 +155,7 @@ static int qlogic_probe(struct pcmcia_device *link) return -ENOMEM; info->p_dev = link; link->priv = info; - link->resource[0]->end = 16; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->config_flags |= CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; link->config_regs = PRESENT_OPTION; return qlogic_config(link); @@ -176,14 +174,11 @@ static void qlogic_detach(struct pcmcia_device *link) /*====================================================================*/ -static int qlogic_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int qlogic_config_check(struct pcmcia_device *p_dev, void *priv_data) { p_dev->io_lines = 10; - p_dev->resource[0]->start = cfg->io.win[0].base; - p_dev->resource[0]->end = cfg->io.win[0].len; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; if (p_dev->resource[0]->start == 0) return -ENODEV; diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c index 7a0bb9aea042..6ceb57c355fa 100644 --- a/drivers/scsi/pcmcia/sym53c500_cs.c +++ b/drivers/scsi/pcmcia/sym53c500_cs.c @@ -683,14 +683,11 @@ static struct scsi_host_template sym53c500_driver_template = { .shost_attrs = SYM53C500_shost_attrs }; -static int SYM53C500_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int SYM53C500_config_check(struct pcmcia_device *p_dev, void *priv_data) { p_dev->io_lines = 10; - p_dev->resource[0]->start = cfg->io.win[0].base; - p_dev->resource[0]->end = cfg->io.win[0].len; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; if (p_dev->resource[0]->start == 0) return -ENODEV; @@ -857,9 +854,7 @@ SYM53C500_probe(struct pcmcia_device *link) return -ENOMEM; info->p_dev = link; link->priv = info; - link->resource[0]->end = 16; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->config_flags |= CONF_ENABLE_IRQ; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; return SYM53C500_config(link); } /* SYM53C500_attach */ diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index a796a93fe39c..422520342936 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -424,41 +424,45 @@ static int pfc_config(struct pcmcia_device *p_dev) return -ENODEV; } -static int simple_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int simple_config_check(struct pcmcia_device *p_dev, void *priv_data) { static const int size_table[2] = { 8, 16 }; int *try = priv_data; - p_dev->io_lines = ((*try & 0x1) == 0) ? - 16 : cf->io.flags & CISTPL_IO_LINES_MASK; + if (p_dev->resource[0]->start == 0) + return -ENODEV; - if ((cf->io.nwin > 0) && (cf->io.win[0].len == size_table[(*try >> 1)]) - && (cf->io.win[0].base != 0)) { - p_dev->resource[0]->start = cf->io.win[0].base; - if (!pcmcia_request_io(p_dev)) - return 0; - } - return -EINVAL; + if ((*try & 0x1) == 0) + p_dev->io_lines = 16; + + if (p_dev->resource[0]->end != size_table[(*try >> 1)]) + return -ENODEV; + + p_dev->resource[0]->end = 8; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + + return pcmcia_request_io(p_dev); } static int simple_config_check_notpicky(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, void *priv_data) { static const unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; int j; - if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { - for (j = 0; j < 5; j++) { - p_dev->resource[0]->start = base[j]; - p_dev->io_lines = base[j] ? 16 : 3; - if (!pcmcia_request_io(p_dev)) - return 0; - } + if (p_dev->io_lines > 3) + return -ENODEV; + + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = 8; + + for (j = 0; j < 5; j++) { + p_dev->resource[0]->start = base[j]; + p_dev->io_lines = base[j] ? 16 : 3; + if (!pcmcia_request_io(p_dev)) + return 0; } return -ENODEV; } @@ -468,12 +472,9 @@ static int simple_config(struct pcmcia_device *link) struct serial_info *info = link->priv; int i = -ENODEV, try; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - link->resource[0]->end = 8; - /* First pass: look for a config entry that looks normal. * Two tries: without IO aliases, then with aliases */ - link->config_flags |= CONF_AUTO_SET_VPP; + link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_SET_IO; for (try = 0; try < 4; try++) if (!pcmcia_loop_config(link, simple_config_check, &try)) goto found_port; @@ -503,43 +504,44 @@ found_port: return setup_serial(link, info, link->resource[0]->start, link->irq); } -static int multi_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int multi_config_check(struct pcmcia_device *p_dev, void *priv_data) { - int *base2 = priv_data; + int *multi = priv_data; + + if (p_dev->resource[1]->end) + return -EINVAL; /* The quad port cards have bad CIS's, so just look for a window larger than 8 ports and assume it will be right */ - if ((cf->io.nwin == 1) && (cf->io.win[0].len > 8)) { - p_dev->resource[0]->start = cf->io.win[0].base; - p_dev->io_lines = cf->io.flags & CISTPL_IO_LINES_MASK; - if (!pcmcia_request_io(p_dev)) { - *base2 = p_dev->resource[0]->start + 8; - return 0; - } - } - return -ENODEV; + if (p_dev->resource[0]->end <= 8) + return -EINVAL; + + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = *multi * 8; + + if (pcmcia_request_io(p_dev)) + return -ENODEV; + return 0; } static int multi_config_check_notpicky(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, void *priv_data) { int *base2 = priv_data; - if (cf->io.nwin == 2) { - p_dev->resource[0]->start = cf->io.win[0].base; - p_dev->resource[1]->start = cf->io.win[1].base; - p_dev->io_lines = cf->io.flags & CISTPL_IO_LINES_MASK; - if (!pcmcia_request_io(p_dev)) { - *base2 = p_dev->resource[1]->start; - return 0; - } - } - return -ENODEV; + if (!p_dev->resource[0]->end || !p_dev->resource[1]->end) + return -ENODEV; + + p_dev->resource[0]->end = p_dev->resource[1]->end = 8; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + + if (pcmcia_request_io(p_dev)) + return -ENODEV; + + *base2 = p_dev->resource[0]->start + 8; + return 0; } static int multi_config(struct pcmcia_device *link) @@ -547,12 +549,12 @@ static int multi_config(struct pcmcia_device *link) struct serial_info *info = link->priv; int i, base2 = 0; + link->config_flags |= CONF_AUTO_SET_IO; /* First, look for a generic full-sized window */ - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - link->resource[0]->end = info->multi * 8; - if (pcmcia_loop_config(link, multi_config_check, &base2)) { + if (!pcmcia_loop_config(link, multi_config_check, &info->multi)) + base2 = link->resource[0]->start + 8; + else { /* If that didn't work, look for two windows */ - link->resource[0]->end = link->resource[1]->end = 8; info->multi = 2; if (pcmcia_loop_config(link, multi_config_check_notpicky, &base2)) { @@ -587,7 +589,7 @@ static int multi_config(struct pcmcia_device *link) link->config_index == 3) { err = setup_serial(link, info, base2, link->irq); - base2 = link->resource[0]->start;; + base2 = link->resource[0]->start; } else { err = setup_serial(link, info, link->resource[0]->start, link->irq); @@ -611,18 +613,18 @@ static int multi_config(struct pcmcia_device *link) return 0; } -static int serial_check_for_multi(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int serial_check_for_multi(struct pcmcia_device *p_dev, void *priv_data) { struct serial_info *info = p_dev->priv; - if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0)) - info->multi = cf->io.win[0].len >> 3; + if (!p_dev->resource[0]->end) + return -EINVAL; + + if ((!p_dev->resource[1]->end) && (p_dev->resource[0]->end % 8 == 0)) + info->multi = p_dev->resource[0]->end >> 3; - if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) && - (cf->io.win[1].len == 8)) + if ((p_dev->resource[1]->end) && (p_dev->resource[0]->end == 8) + && (p_dev->resource[1]->end == 8)) info->multi = 2; return 0; /* break */ diff --git a/drivers/staging/comedi/drivers/cb_das16_cs.c b/drivers/staging/comedi/drivers/cb_das16_cs.c index 678fbf67d7a8..c43c68922c74 100644 --- a/drivers/staging/comedi/drivers/cb_das16_cs.c +++ b/drivers/staging/comedi/drivers/cb_das16_cs.c @@ -710,36 +710,12 @@ static void das16cs_pcmcia_detach(struct pcmcia_device *link) static int das16cs_pcmcia_config_loop(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, void *priv_data) { - if (cfg->index == 0) + if (p_dev->config_index == 0) return -EINVAL; - /* Do we need to allocate an interrupt? */ - p_dev->config_flags |= CONF_ENABLE_IRQ; - - /* IO window settings */ - p_dev->resource[0]->end = p_dev->resource[1]->end = 0; - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= - pcmcia_io_cfg_data_width(io->flags); - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - if (io->nwin > 1) { - p_dev->resource[1]->flags = p_dev->resource[0]->flags; - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - /* This reserves IO space but doesn't actually enable it */ - return pcmcia_request_io(p_dev); - } - - return 0; + return pcmcia_request_io(p_dev); } static void das16cs_pcmcia_config(struct pcmcia_device *link) @@ -748,6 +724,9 @@ static void das16cs_pcmcia_config(struct pcmcia_device *link) dev_dbg(&link->dev, "das16cs_pcmcia_config\n"); + /* Do we need to allocate an interrupt? */ + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + ret = pcmcia_loop_config(link, das16cs_pcmcia_config_loop, NULL); if (ret) { dev_warn(&link->dev, "no configuration found\n"); diff --git a/drivers/staging/comedi/drivers/das08_cs.c b/drivers/staging/comedi/drivers/das08_cs.c index 12a96b7a43ad..d3959093381b 100644 --- a/drivers/staging/comedi/drivers/das08_cs.c +++ b/drivers/staging/comedi/drivers/das08_cs.c @@ -192,35 +192,12 @@ static void das08_pcmcia_detach(struct pcmcia_device *link) static int das08_pcmcia_config_loop(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, void *priv_data) { - if (cfg->index == 0) - return -ENODEV; - - /* Do we need to allocate an interrupt? */ - p_dev->config_flags |= CONF_ENABLE_IRQ; - - /* IO window settings */ - p_dev->resource[0]->end = p_dev->resource[1]->end = 0; - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= - pcmcia_io_cfg_data_width(io->flags); - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - if (io->nwin > 1) { - p_dev->resource[1]->flags = p_dev->resource[0]->flags; - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - /* This reserves IO space but doesn't actually enable it */ - return pcmcia_request_io(p_dev); - } - return 0; + if (p_dev->config_index == 0) + return -EINVAL; + + return pcmcia_request_io(p_dev); } @@ -238,6 +215,8 @@ static void das08_pcmcia_config(struct pcmcia_device *link) dev_dbg(&link->dev, "das08_pcmcia_config\n"); + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + ret = pcmcia_loop_config(link, das08_pcmcia_config_loop, NULL); if (ret) { dev_warn(&link->dev, "no configuration found\n"); diff --git a/drivers/staging/comedi/drivers/ni_daq_700.c b/drivers/staging/comedi/drivers/ni_daq_700.c index f22dc0f2a8d6..7129b0ca4c8b 100644 --- a/drivers/staging/comedi/drivers/ni_daq_700.c +++ b/drivers/staging/comedi/drivers/ni_daq_700.c @@ -530,35 +530,12 @@ static void dio700_cs_detach(struct pcmcia_device *link) ======================================================================*/ static int dio700_pcmcia_config_loop(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, void *priv_data) { - if (cfg->index == 0) - return -ENODEV; - - /* IO window settings */ - p_dev->resource[0]->end = p_dev->resource[1]->end = 0; - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= - pcmcia_io_cfg_data_width(io->flags); - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - if (io->nwin > 1) { - p_dev->resource[1]->flags = p_dev->resource[0]->flags; - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - /* This reserves IO space but doesn't actually enable it */ - if (pcmcia_request_io(p_dev) != 0) - return -ENODEV; - } + if (p_dev->config_index == 0) + return -EINVAL; - /* If we got this far, we're cool! */ - return 0; + return pcmcia_request_io(p_dev); } static void dio700_config(struct pcmcia_device *link) @@ -570,7 +547,8 @@ static void dio700_config(struct pcmcia_device *link) dev_dbg(&link->dev, "dio700_config\n"); - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_AUDIO; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_AUDIO | + CONF_AUTO_SET_IO; ret = pcmcia_loop_config(link, dio700_pcmcia_config_loop, NULL); if (ret) { diff --git a/drivers/staging/comedi/drivers/ni_daq_dio24.c b/drivers/staging/comedi/drivers/ni_daq_dio24.c index 6dc2b06064cd..4defdda2bfaf 100644 --- a/drivers/staging/comedi/drivers/ni_daq_dio24.c +++ b/drivers/staging/comedi/drivers/ni_daq_dio24.c @@ -282,35 +282,12 @@ static void dio24_cs_detach(struct pcmcia_device *link) ======================================================================*/ static int dio24_pcmcia_config_loop(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, void *priv_data) { - if (cfg->index == 0) - return -ENODEV; - - /* IO window settings */ - p_dev->resource[0]->end = p_dev->resource[1]->end = 0; - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= - pcmcia_io_cfg_data_width(io->flags); - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - if (io->nwin > 1) { - p_dev->resource[1]->flags = p_dev->resource[0]->flags; - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - /* This reserves IO space but doesn't actually enable it */ - if (pcmcia_request_io(p_dev) != 0) - return -ENODEV; - } + if (p_dev->config_index == 0) + return -EINVAL; - /* If we got this far, we're cool! */ - return 0; + return pcmcia_request_io(p_dev); } static void dio24_config(struct pcmcia_device *link) @@ -321,7 +298,8 @@ static void dio24_config(struct pcmcia_device *link) dev_dbg(&link->dev, "dio24_config\n"); - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_AUDIO; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_AUDIO | + CONF_AUTO_SET_IO; ret = pcmcia_loop_config(link, dio24_pcmcia_config_loop, NULL); if (ret) { diff --git a/drivers/staging/comedi/drivers/ni_labpc_cs.c b/drivers/staging/comedi/drivers/ni_labpc_cs.c index 6eacbd70e2e9..5123b3131c51 100644 --- a/drivers/staging/comedi/drivers/ni_labpc_cs.c +++ b/drivers/staging/comedi/drivers/ni_labpc_cs.c @@ -261,35 +261,12 @@ static void labpc_cs_detach(struct pcmcia_device *link) ======================================================================*/ static int labpc_pcmcia_config_loop(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, void *priv_data) { - if (cfg->index == 0) - return -ENODEV; - - /* IO window settings */ - p_dev->resource[0]->end = p_dev->resource[1]->end = 0; - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= - pcmcia_io_cfg_data_width(io->flags); - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - if (io->nwin > 1) { - p_dev->resource[1]->flags = p_dev->resource[0]->flags; - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - /* This reserves IO space but doesn't actually enable it */ - if (pcmcia_request_io(p_dev) != 0) - return -ENODEV; - } + if (p_dev->config_index == 0) + return -EINVAL; - /* If we got this far, we're cool! */ - return 0; + return pcmcia_request_io(p_dev); } @@ -300,7 +277,7 @@ static void labpc_config(struct pcmcia_device *link) dev_dbg(&link->dev, "labpc_config\n"); link->config_flags |= CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ | - CONF_AUTO_AUDIO; + CONF_AUTO_AUDIO | CONF_AUTO_SET_IO; ret = pcmcia_loop_config(link, labpc_pcmcia_config_loop, NULL); if (ret) { @@ -316,7 +293,6 @@ static void labpc_config(struct pcmcia_device *link) the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. */ - p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ; ret = pcmcia_enable_device(link); if (ret) goto failed; diff --git a/drivers/staging/comedi/drivers/ni_mio_cs.c b/drivers/staging/comedi/drivers/ni_mio_cs.c index da4e2a21b19a..f1e31d3e12bc 100644 --- a/drivers/staging/comedi/drivers/ni_mio_cs.c +++ b/drivers/staging/comedi/drivers/ni_mio_cs.c @@ -262,10 +262,6 @@ static struct pcmcia_device *cur_dev = NULL; static int cs_attach(struct pcmcia_device *link) { - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; - link->resource[0]->end = 16; - link->config_flags |= CONF_ENABLE_IRQ; - cur_dev = link; mio_cs_config(link); @@ -299,15 +295,12 @@ static int mio_cs_resume(struct pcmcia_device *link) } -static int mio_pcmcia_config_loop(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int mio_pcmcia_config_loop(struct pcmcia_device *p_dev, void *priv_data) { int base, ret; - p_dev->resource[0]->end = cfg->io.win[0].len; - p_dev->io_lines = cfg->io.flags & CISTPL_IO_LINES_MASK; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; for (base = 0x000; base < 0x400; base += 0x20) { p_dev->resource[0]->start = base; @@ -324,6 +317,7 @@ static void mio_cs_config(struct pcmcia_device *link) int ret; DPRINTK("mio_cs_config(link=%p)\n", link); + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; ret = pcmcia_loop_config(link, mio_pcmcia_config_loop, NULL); if (ret) { diff --git a/drivers/staging/comedi/drivers/quatech_daqp_cs.c b/drivers/staging/comedi/drivers/quatech_daqp_cs.c index 03a72d7ac676..afd283d0e711 100644 --- a/drivers/staging/comedi/drivers/quatech_daqp_cs.c +++ b/drivers/staging/comedi/drivers/quatech_daqp_cs.c @@ -1068,35 +1068,11 @@ static void daqp_cs_detach(struct pcmcia_device *link) ======================================================================*/ -static int daqp_pcmcia_config_loop(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int daqp_pcmcia_config_loop(struct pcmcia_device *p_dev, void *priv_data) { - if (cfg->index == 0) - return -ENODEV; + if (p_dev->config_index == 0) + return -EINVAL; - /* Do we need to allocate an interrupt? */ - p_dev->config_flags |= CONF_ENABLE_IRQ; - - /* IO window settings */ - p_dev->resource[0]->end = p_dev->resource[1]->end = 0; - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= - pcmcia_io_cfg_data_width(io->flags); - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - if (io->nwin > 1) { - p_dev->resource[1]->flags = p_dev->resource[0]->flags; - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - } - - /* This reserves IO space but doesn't actually enable it */ return pcmcia_request_io(p_dev); } @@ -1106,6 +1082,8 @@ static void daqp_cs_config(struct pcmcia_device *link) dev_dbg(&link->dev, "daqp_cs_config\n"); + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + ret = pcmcia_loop_config(link, daqp_pcmcia_config_loop, NULL); if (ret) { dev_warn(&link->dev, "no configuration found\n"); diff --git a/drivers/telephony/ixj_pcmcia.c b/drivers/telephony/ixj_pcmcia.c index 670a76bf5164..76edd39525de 100644 --- a/drivers/telephony/ixj_pcmcia.c +++ b/drivers/telephony/ixj_pcmcia.c @@ -31,8 +31,6 @@ static int ixj_probe(struct pcmcia_device *p_dev) { dev_dbg(&p_dev->dev, "ixj_attach()\n"); /* Create new ixj device */ - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; p_dev->priv = kzalloc(sizeof(struct ixj_info_t), GFP_KERNEL); if (!p_dev->priv) { return -ENOMEM; @@ -109,36 +107,28 @@ failed: return; } -static int ixj_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int ixj_config_check(struct pcmcia_device *p_dev, void *priv_data) { - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - p_dev->io_lines = 3; - if (io->nwin == 2) { - p_dev->resource[1]->start = io->win[1].base; - p_dev->resource[1]->end = io->win[1].len; - } - if (!pcmcia_request_io(p_dev)) - return 0; - } - return -ENODEV; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->io_lines = 3; + + return pcmcia_request_io(p_dev); } static int ixj_config(struct pcmcia_device * link) { IXJ *j; ixj_info_t *info; - cistpl_cftable_entry_t dflt = { 0 }; info = link->priv; dev_dbg(&link->dev, "ixj_config\n"); - if (pcmcia_loop_config(link, ixj_config_check, &dflt)) + link->config_flags = CONF_AUTO_SET_IO; + + if (pcmcia_loop_config(link, ixj_config_check, NULL)) goto failed; if (pcmcia_enable_device(link)) diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c index d9606293c1af..81d7eeacdf6a 100644 --- a/drivers/usb/host/sl811_cs.c +++ b/drivers/usb/host/sl811_cs.c @@ -131,28 +131,12 @@ static void sl811_cs_release(struct pcmcia_device * link) platform_device_unregister(&platform_dev); } -static int sl811_cs_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - void *priv_data) +static int sl811_cs_config_check(struct pcmcia_device *p_dev, void *priv_data) { - if (cfg->index == 0) - return -ENODEV; + if (p_dev->config_index == 0) + return -EINVAL; - /* IO window settings */ - p_dev->resource[0]->end = p_dev->resource[1]->end = 0; - if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { - cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; - p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK; - - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - p_dev->resource[0]->start = io->win[0].base; - p_dev->resource[0]->end = io->win[0].len; - - return pcmcia_request_io(p_dev); - } - pcmcia_disable_device(p_dev); - return -ENODEV; + return pcmcia_request_io(p_dev); } @@ -164,7 +148,7 @@ static int sl811_cs_config(struct pcmcia_device *link) dev_dbg(&link->dev, "sl811_cs_config\n"); link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | - CONF_AUTO_CHECK_VCC; + CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO; if (pcmcia_loop_config(link, sl811_cs_config_check, NULL)) goto failed; diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h index 0577e5f10304..0b8c8d45df47 100644 --- a/include/pcmcia/ds.h +++ b/include/pcmcia/ds.h @@ -93,6 +93,7 @@ struct pcmcia_device { /* device setup */ unsigned int irq; struct resource *resource[PCMCIA_NUM_RESOURCES]; + resource_size_t card_addr; /* for the 1st IOMEM resource */ unsigned int vpp; unsigned int config_flags; /* CONF_ENABLE_ flags below */ @@ -175,8 +176,6 @@ int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse); /* loop CIS entries for valid configuration */ int pcmcia_loop_config(struct pcmcia_device *p_dev, int (*conf_check) (struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, void *priv_data), void *priv_data); @@ -225,16 +224,6 @@ void pcmcia_disable_device(struct pcmcia_device *p_dev); #define IO_DATA_PATH_WIDTH_16 0x08 #define IO_DATA_PATH_WIDTH_AUTO 0x10 -/* convert flag found in cfgtable to data path width parameter */ -static inline int pcmcia_io_cfg_data_width(unsigned int flags) -{ - if (!(flags & CISTPL_IO_8BIT)) - return IO_DATA_PATH_WIDTH_16; - if (!(flags & CISTPL_IO_16BIT)) - return IO_DATA_PATH_WIDTH_8; - return IO_DATA_PATH_WIDTH_AUTO; -} - /* IO memory */ #define WIN_MEMORY_TYPE_CM 0x00 /* default */ #define WIN_MEMORY_TYPE_AM 0x20 /* MAP_ATTRIB */ @@ -264,16 +253,17 @@ static inline int pcmcia_io_cfg_data_width(unsigned int flags) #define PRESENT_IOSIZE 0x200 /* flags to be passed to pcmcia_enable_device() */ -#define CONF_ENABLE_IRQ 0x01 -#define CONF_ENABLE_SPKR 0x02 -#define CONF_ENABLE_PULSE_IRQ 0x04 -#define CONF_ENABLE_ESR 0x08 +#define CONF_ENABLE_IRQ 0x0001 +#define CONF_ENABLE_SPKR 0x0002 +#define CONF_ENABLE_PULSE_IRQ 0x0004 +#define CONF_ENABLE_ESR 0x0008 /* flags used by pcmcia_loop_config() autoconfiguration */ -#define CONF_AUTO_CHECK_VCC 0x10 /* check for matching Vcc? */ -#define CONF_AUTO_SET_VPP 0x20 /* set Vpp? */ -#define CONF_AUTO_AUDIO 0x40 /* enable audio line? */ - +#define CONF_AUTO_CHECK_VCC 0x0100 /* check for matching Vcc? */ +#define CONF_AUTO_SET_VPP 0x0200 /* set Vpp? */ +#define CONF_AUTO_AUDIO 0x0400 /* enable audio line? */ +#define CONF_AUTO_SET_IO 0x0800 /* set ->resource[0,1] */ +#define CONF_AUTO_SET_IOMEM 0x1000 /* set ->resource[2] */ #endif /* __KERNEL__ */ -- cgit v1.2.3-70-g09d2 From 2116b7a473bf1c8d26998b477c294e7fe294921f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 4 Oct 2010 22:55:57 +0200 Subject: smbfs: move to drivers/staging smbfs has been scheduled for removal in 2.6.27, so maybe we can now move it to drivers/staging on the way out. smbfs still uses the big kernel lock and nobody is going to fix that, so we should be getting rid of it soon. This removes the 32 bit compat mount and ioctl handling code, which is implemented in common fs code, and moves all smbfs related files into drivers/staging/smbfs. Signed-off-by: Arnd Bergmann Acked-by: Jeff Layton Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/00-INDEX | 2 - Documentation/filesystems/smbfs.txt | 8 - Documentation/ioctl/ioctl-number.txt | 2 +- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/smbfs/Kconfig | 55 + drivers/staging/smbfs/Makefile | 18 + drivers/staging/smbfs/TODO | 8 + drivers/staging/smbfs/cache.c | 208 ++ drivers/staging/smbfs/dir.c | 702 +++++++ drivers/staging/smbfs/file.c | 453 +++++ drivers/staging/smbfs/getopt.c | 64 + drivers/staging/smbfs/getopt.h | 14 + drivers/staging/smbfs/inode.c | 839 ++++++++ drivers/staging/smbfs/ioctl.c | 68 + drivers/staging/smbfs/proc.c | 3506 +++++++++++++++++++++++++++++++++ drivers/staging/smbfs/proto.h | 87 + drivers/staging/smbfs/request.c | 817 ++++++++ drivers/staging/smbfs/request.h | 70 + drivers/staging/smbfs/smb.h | 118 ++ drivers/staging/smbfs/smb_debug.h | 34 + drivers/staging/smbfs/smb_fs.h | 153 ++ drivers/staging/smbfs/smb_fs_i.h | 37 + drivers/staging/smbfs/smb_fs_sb.h | 100 + drivers/staging/smbfs/smb_mount.h | 65 + drivers/staging/smbfs/smbfs.txt | 8 + drivers/staging/smbfs/smbiod.c | 343 ++++ drivers/staging/smbfs/smbno.h | 363 ++++ drivers/staging/smbfs/sock.c | 385 ++++ drivers/staging/smbfs/symlink.c | 67 + fs/Kconfig | 1 - fs/Makefile | 1 - fs/compat.c | 31 +- fs/compat_ioctl.c | 26 - fs/smbfs/Kconfig | 55 - fs/smbfs/Makefile | 18 - fs/smbfs/cache.c | 208 -- fs/smbfs/dir.c | 702 ------- fs/smbfs/file.c | 454 ----- fs/smbfs/getopt.c | 64 - fs/smbfs/getopt.h | 14 - fs/smbfs/inode.c | 839 -------- fs/smbfs/ioctl.c | 69 - fs/smbfs/proc.c | 3507 ---------------------------------- fs/smbfs/proto.h | 87 - fs/smbfs/request.c | 818 -------- fs/smbfs/request.h | 70 - fs/smbfs/smb_debug.h | 34 - fs/smbfs/smbiod.c | 344 ---- fs/smbfs/sock.c | 386 ---- fs/smbfs/symlink.c | 68 - include/linux/Kbuild | 4 - include/linux/smb.h | 118 -- include/linux/smb_fs.h | 153 -- include/linux/smb_fs_i.h | 37 - include/linux/smb_fs_sb.h | 100 - include/linux/smb_mount.h | 65 - include/linux/smbno.h | 363 ---- 58 files changed, 8587 insertions(+), 8646 deletions(-) delete mode 100644 Documentation/filesystems/smbfs.txt create mode 100644 drivers/staging/smbfs/Kconfig create mode 100644 drivers/staging/smbfs/Makefile create mode 100644 drivers/staging/smbfs/TODO create mode 100644 drivers/staging/smbfs/cache.c create mode 100644 drivers/staging/smbfs/dir.c create mode 100644 drivers/staging/smbfs/file.c create mode 100644 drivers/staging/smbfs/getopt.c create mode 100644 drivers/staging/smbfs/getopt.h create mode 100644 drivers/staging/smbfs/inode.c create mode 100644 drivers/staging/smbfs/ioctl.c create mode 100644 drivers/staging/smbfs/proc.c create mode 100644 drivers/staging/smbfs/proto.h create mode 100644 drivers/staging/smbfs/request.c create mode 100644 drivers/staging/smbfs/request.h create mode 100644 drivers/staging/smbfs/smb.h create mode 100644 drivers/staging/smbfs/smb_debug.h create mode 100644 drivers/staging/smbfs/smb_fs.h create mode 100644 drivers/staging/smbfs/smb_fs_i.h create mode 100644 drivers/staging/smbfs/smb_fs_sb.h create mode 100644 drivers/staging/smbfs/smb_mount.h create mode 100644 drivers/staging/smbfs/smbfs.txt create mode 100644 drivers/staging/smbfs/smbiod.c create mode 100644 drivers/staging/smbfs/smbno.h create mode 100644 drivers/staging/smbfs/sock.c create mode 100644 drivers/staging/smbfs/symlink.c delete mode 100644 fs/smbfs/Kconfig delete mode 100644 fs/smbfs/Makefile delete mode 100644 fs/smbfs/cache.c delete mode 100644 fs/smbfs/dir.c delete mode 100644 fs/smbfs/file.c delete mode 100644 fs/smbfs/getopt.c delete mode 100644 fs/smbfs/getopt.h delete mode 100644 fs/smbfs/inode.c delete mode 100644 fs/smbfs/ioctl.c delete mode 100644 fs/smbfs/proc.c delete mode 100644 fs/smbfs/proto.h delete mode 100644 fs/smbfs/request.c delete mode 100644 fs/smbfs/request.h delete mode 100644 fs/smbfs/smb_debug.h delete mode 100644 fs/smbfs/smbiod.c delete mode 100644 fs/smbfs/sock.c delete mode 100644 fs/smbfs/symlink.c delete mode 100644 include/linux/smb.h delete mode 100644 include/linux/smb_fs.h delete mode 100644 include/linux/smb_fs_i.h delete mode 100644 include/linux/smb_fs_sb.h delete mode 100644 include/linux/smb_mount.h delete mode 100644 include/linux/smbno.h (limited to 'Documentation') diff --git a/Documentation/filesystems/00-INDEX b/Documentation/filesystems/00-INDEX index 4303614b5add..8c624a18f67d 100644 --- a/Documentation/filesystems/00-INDEX +++ b/Documentation/filesystems/00-INDEX @@ -96,8 +96,6 @@ seq_file.txt - how to use the seq_file API sharedsubtree.txt - a description of shared subtrees for namespaces. -smbfs.txt - - info on using filesystems with the SMB protocol (Win 3.11 and NT). spufs.txt - info and mount options for the SPU filesystem used on Cell. sysfs-pci.txt diff --git a/Documentation/filesystems/smbfs.txt b/Documentation/filesystems/smbfs.txt deleted file mode 100644 index 194fb0decd2c..000000000000 --- a/Documentation/filesystems/smbfs.txt +++ /dev/null @@ -1,8 +0,0 @@ -Smbfs is a filesystem that implements the SMB protocol, which is the -protocol used by Windows for Workgroups, Windows 95 and Windows NT. -Smbfs was inspired by Samba, the program written by Andrew Tridgell -that turns any Unix host into a file server for DOS or Windows clients. - -Smbfs is a SMB client, but uses parts of samba for its operation. For -more info on samba, including documentation, please go to -http://www.samba.org/ and then on to your nearest mirror. diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 33223ff121d8..d15834a6e461 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -259,7 +259,7 @@ Code Seq#(hex) Include File Comments 't' 00-7F linux/if_ppp.h 't' 80-8F linux/isdn_ppp.h 't' 90 linux/toshiba.h -'u' 00-1F linux/smb_fs.h +'u' 00-1F linux/smb_fs.h gone 'v' all linux/videodev.h conflict! 'v' 00-1F linux/ext2_fs.h conflict! 'v' 00-1F linux/fs.h conflict! diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 01503536e457..4a9190808b73 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -149,6 +149,8 @@ source "drivers/staging/msm/Kconfig" source "drivers/staging/lirc/Kconfig" +source "drivers/staging/smbfs/Kconfig" + source "drivers/staging/easycap/Kconfig" source "drivers/staging/solo6x10/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index de5c0e5a99ed..7cb02a8e9f77 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_AUTOFS_FS) += autofs/ obj-$(CONFIG_IDE_PHISON) += phison/ obj-$(CONFIG_LINE6_USB) += line6/ obj-$(CONFIG_USB_SERIAL_QUATECH2) += serqt_usb2/ +obj-$(CONFIG_SMB_FS) += smbfs/ obj-$(CONFIG_USB_SERIAL_QUATECH_USB2) += quatech_usb2/ obj-$(CONFIG_OCTEON_ETHERNET) += octeon/ obj-$(CONFIG_VT6655) += vt6655/ diff --git a/drivers/staging/smbfs/Kconfig b/drivers/staging/smbfs/Kconfig new file mode 100644 index 000000000000..e668127c8b2e --- /dev/null +++ b/drivers/staging/smbfs/Kconfig @@ -0,0 +1,55 @@ +config SMB_FS + tristate "SMB file system support (OBSOLETE, please use CIFS)" + depends on INET + select NLS + help + SMB (Server Message Block) is the protocol Windows for Workgroups + (WfW), Windows 95/98, Windows NT and OS/2 Lan Manager use to share + files and printers over local networks. Saying Y here allows you to + mount their file systems (often called "shares" in this context) and + access them just like any other Unix directory. Currently, this + works only if the Windows machines use TCP/IP as the underlying + transport protocol, and not NetBEUI. For details, read + and the SMB-HOWTO, + available from . + + Note: if you just want your box to act as an SMB *server* and make + files and printing services available to Windows clients (which need + to have a TCP/IP stack), you don't need to say Y here; you can use + the program SAMBA (available from ) + for that. + + General information about how to connect Linux, Windows machines and + Macs is on the WWW at . + + To compile the SMB support as a module, choose M here: + the module will be called smbfs. Most people say N, however. + +config SMB_NLS_DEFAULT + bool "Use a default NLS" + depends on SMB_FS + help + Enabling this will make smbfs use nls translations by default. You + need to specify the local charset (CONFIG_NLS_DEFAULT) in the nls + settings and you need to give the default nls for the SMB server as + CONFIG_SMB_NLS_REMOTE. + + The nls settings can be changed at mount time, if your smbmount + supports that, using the codepage and iocharset parameters. + + smbmount from samba 2.2.0 or later supports this. + +config SMB_NLS_REMOTE + string "Default Remote NLS Option" + depends on SMB_NLS_DEFAULT + default "cp437" + help + This setting allows you to specify a default value for which + codepage the server uses. If this field is left blank no + translations will be done by default. The local codepage/charset + default to CONFIG_NLS_DEFAULT. + + The nls settings can be changed at mount time, if your smbmount + supports that, using the codepage and iocharset parameters. + + smbmount from samba 2.2.0 or later supports this. diff --git a/drivers/staging/smbfs/Makefile b/drivers/staging/smbfs/Makefile new file mode 100644 index 000000000000..4faf8c4722c3 --- /dev/null +++ b/drivers/staging/smbfs/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for the linux smb-filesystem routines. +# + +obj-$(CONFIG_SMB_FS) += smbfs.o + +smbfs-objs := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o \ + symlink.o smbiod.o request.o + +# If you want debugging output, you may add these flags to the EXTRA_CFLAGS +# SMBFS_PARANOIA should normally be enabled. + +EXTRA_CFLAGS += -DSMBFS_PARANOIA +#EXTRA_CFLAGS += -DSMBFS_DEBUG +#EXTRA_CFLAGS += -DSMBFS_DEBUG_VERBOSE +#EXTRA_CFLAGS += -DDEBUG_SMB_TIMESTAMP +#EXTRA_CFLAGS += -Werror + diff --git a/drivers/staging/smbfs/TODO b/drivers/staging/smbfs/TODO new file mode 100644 index 000000000000..24f4d29d53ac --- /dev/null +++ b/drivers/staging/smbfs/TODO @@ -0,0 +1,8 @@ +smbfs is on its way out of the kernel, it has been replaced +by cifs several years ago. + +The smbfs code uses the big kernel lock which +is getting deprecated. + +Users that find smbfs to work but not cifs should contact +the CIFS developers on linux-cifs@vger.kernel.org. diff --git a/drivers/staging/smbfs/cache.c b/drivers/staging/smbfs/cache.c new file mode 100644 index 000000000000..dbb98658148b --- /dev/null +++ b/drivers/staging/smbfs/cache.c @@ -0,0 +1,208 @@ +/* + * cache.c + * + * Copyright (C) 1997 by Bill Hawes + * + * Routines to support directory cacheing using the page cache. + * This cache code is almost directly taken from ncpfs. + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "smb_fs.h" +#include "smb_debug.h" +#include "proto.h" + +/* + * Force the next attempt to use the cache to be a timeout. + * If we can't find the page that's fine, it will cause a refresh. + */ +void +smb_invalid_dir_cache(struct inode * dir) +{ + struct smb_sb_info *server = server_from_inode(dir); + union smb_dir_cache *cache = NULL; + struct page *page = NULL; + + page = grab_cache_page(&dir->i_data, 0); + if (!page) + goto out; + + if (!PageUptodate(page)) + goto out_unlock; + + cache = kmap(page); + cache->head.time = jiffies - SMB_MAX_AGE(server); + + kunmap(page); + SetPageUptodate(page); +out_unlock: + unlock_page(page); + page_cache_release(page); +out: + return; +} + +/* + * Mark all dentries for 'parent' as invalid, forcing them to be re-read + */ +void +smb_invalidate_dircache_entries(struct dentry *parent) +{ + struct smb_sb_info *server = server_from_dentry(parent); + struct list_head *next; + struct dentry *dentry; + + spin_lock(&dcache_lock); + next = parent->d_subdirs.next; + while (next != &parent->d_subdirs) { + dentry = list_entry(next, struct dentry, d_u.d_child); + dentry->d_fsdata = NULL; + smb_age_dentry(server, dentry); + next = next->next; + } + spin_unlock(&dcache_lock); +} + +/* + * dget, but require that fpos and parent matches what the dentry contains. + * dentry is not known to be a valid pointer at entry. + */ +struct dentry * +smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) +{ + struct dentry *dent = dentry; + struct list_head *next; + + if (d_validate(dent, parent)) { + if (dent->d_name.len <= SMB_MAXNAMELEN && + (unsigned long)dent->d_fsdata == fpos) { + if (!dent->d_inode) { + dput(dent); + dent = NULL; + } + return dent; + } + dput(dent); + } + + /* If a pointer is invalid, we search the dentry. */ + spin_lock(&dcache_lock); + next = parent->d_subdirs.next; + while (next != &parent->d_subdirs) { + dent = list_entry(next, struct dentry, d_u.d_child); + if ((unsigned long)dent->d_fsdata == fpos) { + if (dent->d_inode) + dget_locked(dent); + else + dent = NULL; + goto out_unlock; + } + next = next->next; + } + dent = NULL; +out_unlock: + spin_unlock(&dcache_lock); + return dent; +} + + +/* + * Create dentry/inode for this file and add it to the dircache. + */ +int +smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, + struct smb_cache_control *ctrl, struct qstr *qname, + struct smb_fattr *entry) +{ + struct dentry *newdent, *dentry = filp->f_path.dentry; + struct inode *newino, *inode = dentry->d_inode; + struct smb_cache_control ctl = *ctrl; + int valid = 0; + int hashed = 0; + ino_t ino = 0; + + qname->hash = full_name_hash(qname->name, qname->len); + + if (dentry->d_op && dentry->d_op->d_hash) + if (dentry->d_op->d_hash(dentry, qname) != 0) + goto end_advance; + + newdent = d_lookup(dentry, qname); + + if (!newdent) { + newdent = d_alloc(dentry, qname); + if (!newdent) + goto end_advance; + } else { + hashed = 1; + memcpy((char *) newdent->d_name.name, qname->name, + newdent->d_name.len); + } + + if (!newdent->d_inode) { + smb_renew_times(newdent); + entry->f_ino = iunique(inode->i_sb, 2); + newino = smb_iget(inode->i_sb, entry); + if (newino) { + smb_new_dentry(newdent); + d_instantiate(newdent, newino); + if (!hashed) + d_rehash(newdent); + } + } else + smb_set_inode_attr(newdent->d_inode, entry); + + if (newdent->d_inode) { + ino = newdent->d_inode->i_ino; + newdent->d_fsdata = (void *) ctl.fpos; + smb_new_dentry(newdent); + } + + if (ctl.idx >= SMB_DIRCACHE_SIZE) { + if (ctl.page) { + kunmap(ctl.page); + SetPageUptodate(ctl.page); + unlock_page(ctl.page); + page_cache_release(ctl.page); + } + ctl.cache = NULL; + ctl.idx -= SMB_DIRCACHE_SIZE; + ctl.ofs += 1; + ctl.page = grab_cache_page(&inode->i_data, ctl.ofs); + if (ctl.page) + ctl.cache = kmap(ctl.page); + } + if (ctl.cache) { + ctl.cache->dentry[ctl.idx] = newdent; + valid = 1; + } + dput(newdent); + +end_advance: + if (!valid) + ctl.valid = 0; + if (!ctl.filled && (ctl.fpos == filp->f_pos)) { + if (!ino) + ino = find_inode_number(dentry, qname); + if (!ino) + ino = iunique(inode->i_sb, 2); + ctl.filled = filldir(dirent, qname->name, qname->len, + filp->f_pos, ino, DT_UNKNOWN); + if (!ctl.filled) + filp->f_pos += 1; + } + ctl.fpos += 1; + ctl.idx += 1; + *ctrl = ctl; + return (ctl.valid || !ctl.filled); +} diff --git a/drivers/staging/smbfs/dir.c b/drivers/staging/smbfs/dir.c new file mode 100644 index 000000000000..936b3bb2099c --- /dev/null +++ b/drivers/staging/smbfs/dir.c @@ -0,0 +1,702 @@ +/* + * dir.c + * + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "smb_fs.h" +#include "smb_mount.h" +#include "smbno.h" + +#include "smb_debug.h" +#include "proto.h" + +static int smb_readdir(struct file *, void *, filldir_t); +static int smb_dir_open(struct inode *, struct file *); + +static struct dentry *smb_lookup(struct inode *, struct dentry *, struct nameidata *); +static int smb_create(struct inode *, struct dentry *, int, struct nameidata *); +static int smb_mkdir(struct inode *, struct dentry *, int); +static int smb_rmdir(struct inode *, struct dentry *); +static int smb_unlink(struct inode *, struct dentry *); +static int smb_rename(struct inode *, struct dentry *, + struct inode *, struct dentry *); +static int smb_make_node(struct inode *,struct dentry *,int,dev_t); +static int smb_link(struct dentry *, struct inode *, struct dentry *); + +const struct file_operations smb_dir_operations = +{ + .llseek = generic_file_llseek, + .read = generic_read_dir, + .readdir = smb_readdir, + .unlocked_ioctl = smb_ioctl, + .open = smb_dir_open, +}; + +const struct inode_operations smb_dir_inode_operations = +{ + .create = smb_create, + .lookup = smb_lookup, + .unlink = smb_unlink, + .mkdir = smb_mkdir, + .rmdir = smb_rmdir, + .rename = smb_rename, + .getattr = smb_getattr, + .setattr = smb_notify_change, +}; + +const struct inode_operations smb_dir_inode_operations_unix = +{ + .create = smb_create, + .lookup = smb_lookup, + .unlink = smb_unlink, + .mkdir = smb_mkdir, + .rmdir = smb_rmdir, + .rename = smb_rename, + .getattr = smb_getattr, + .setattr = smb_notify_change, + .symlink = smb_symlink, + .mknod = smb_make_node, + .link = smb_link, +}; + +/* + * Read a directory, using filldir to fill the dirent memory. + * smb_proc_readdir does the actual reading from the smb server. + * + * The cache code is almost directly taken from ncpfs + */ +static int +smb_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct dentry *dentry = filp->f_path.dentry; + struct inode *dir = dentry->d_inode; + struct smb_sb_info *server = server_from_dentry(dentry); + union smb_dir_cache *cache = NULL; + struct smb_cache_control ctl; + struct page *page = NULL; + int result; + + ctl.page = NULL; + ctl.cache = NULL; + + VERBOSE("reading %s/%s, f_pos=%d\n", + DENTRY_PATH(dentry), (int) filp->f_pos); + + result = 0; + + lock_kernel(); + + switch ((unsigned int) filp->f_pos) { + case 0: + if (filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR) < 0) + goto out; + filp->f_pos = 1; + /* fallthrough */ + case 1: + if (filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR) < 0) + goto out; + filp->f_pos = 2; + } + + /* + * Make sure our inode is up-to-date. + */ + result = smb_revalidate_inode(dentry); + if (result) + goto out; + + + page = grab_cache_page(&dir->i_data, 0); + if (!page) + goto read_really; + + ctl.cache = cache = kmap(page); + ctl.head = cache->head; + + if (!PageUptodate(page) || !ctl.head.eof) { + VERBOSE("%s/%s, page uptodate=%d, eof=%d\n", + DENTRY_PATH(dentry), PageUptodate(page),ctl.head.eof); + goto init_cache; + } + + if (filp->f_pos == 2) { + if (jiffies - ctl.head.time >= SMB_MAX_AGE(server)) + goto init_cache; + + /* + * N.B. ncpfs checks mtime of dentry too here, we don't. + * 1. common smb servers do not update mtime on dir changes + * 2. it requires an extra smb request + * (revalidate has the same timeout as ctl.head.time) + * + * Instead smbfs invalidates its own cache on local changes + * and remote changes are not seen until timeout. + */ + } + + if (filp->f_pos > ctl.head.end) + goto finished; + + ctl.fpos = filp->f_pos + (SMB_DIRCACHE_START - 2); + ctl.ofs = ctl.fpos / SMB_DIRCACHE_SIZE; + ctl.idx = ctl.fpos % SMB_DIRCACHE_SIZE; + + for (;;) { + if (ctl.ofs != 0) { + ctl.page = find_lock_page(&dir->i_data, ctl.ofs); + if (!ctl.page) + goto invalid_cache; + ctl.cache = kmap(ctl.page); + if (!PageUptodate(ctl.page)) + goto invalid_cache; + } + while (ctl.idx < SMB_DIRCACHE_SIZE) { + struct dentry *dent; + int res; + + dent = smb_dget_fpos(ctl.cache->dentry[ctl.idx], + dentry, filp->f_pos); + if (!dent) + goto invalid_cache; + + res = filldir(dirent, dent->d_name.name, + dent->d_name.len, filp->f_pos, + dent->d_inode->i_ino, DT_UNKNOWN); + dput(dent); + if (res) + goto finished; + filp->f_pos += 1; + ctl.idx += 1; + if (filp->f_pos > ctl.head.end) + goto finished; + } + if (ctl.page) { + kunmap(ctl.page); + SetPageUptodate(ctl.page); + unlock_page(ctl.page); + page_cache_release(ctl.page); + ctl.page = NULL; + } + ctl.idx = 0; + ctl.ofs += 1; + } +invalid_cache: + if (ctl.page) { + kunmap(ctl.page); + unlock_page(ctl.page); + page_cache_release(ctl.page); + ctl.page = NULL; + } + ctl.cache = cache; +init_cache: + smb_invalidate_dircache_entries(dentry); + ctl.head.time = jiffies; + ctl.head.eof = 0; + ctl.fpos = 2; + ctl.ofs = 0; + ctl.idx = SMB_DIRCACHE_START; + ctl.filled = 0; + ctl.valid = 1; +read_really: + result = server->ops->readdir(filp, dirent, filldir, &ctl); + if (result == -ERESTARTSYS && page) + ClearPageUptodate(page); + if (ctl.idx == -1) + goto invalid_cache; /* retry */ + ctl.head.end = ctl.fpos - 1; + ctl.head.eof = ctl.valid; +finished: + if (page) { + cache->head = ctl.head; + kunmap(page); + if (result != -ERESTARTSYS) + SetPageUptodate(page); + unlock_page(page); + page_cache_release(page); + } + if (ctl.page) { + kunmap(ctl.page); + SetPageUptodate(ctl.page); + unlock_page(ctl.page); + page_cache_release(ctl.page); + } +out: + unlock_kernel(); + return result; +} + +static int +smb_dir_open(struct inode *dir, struct file *file) +{ + struct dentry *dentry = file->f_path.dentry; + struct smb_sb_info *server; + int error = 0; + + VERBOSE("(%s/%s)\n", dentry->d_parent->d_name.name, + file->f_path.dentry->d_name.name); + + /* + * Directory timestamps in the core protocol aren't updated + * when a file is added, so we give them a very short TTL. + */ + lock_kernel(); + server = server_from_dentry(dentry); + if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) { + unsigned long age = jiffies - SMB_I(dir)->oldmtime; + if (age > 2*HZ) + smb_invalid_dir_cache(dir); + } + + /* + * Note: in order to allow the smbmount process to open the + * mount point, we only revalidate if the connection is valid or + * if the process is trying to access something other than the root. + */ + if (server->state == CONN_VALID || !IS_ROOT(dentry)) + error = smb_revalidate_inode(dentry); + unlock_kernel(); + return error; +} + +/* + * Dentry operations routines + */ +static int smb_lookup_validate(struct dentry *, struct nameidata *); +static int smb_hash_dentry(struct dentry *, struct qstr *); +static int smb_compare_dentry(struct dentry *, struct qstr *, struct qstr *); +static int smb_delete_dentry(struct dentry *); + +static const struct dentry_operations smbfs_dentry_operations = +{ + .d_revalidate = smb_lookup_validate, + .d_hash = smb_hash_dentry, + .d_compare = smb_compare_dentry, + .d_delete = smb_delete_dentry, +}; + +static const struct dentry_operations smbfs_dentry_operations_case = +{ + .d_revalidate = smb_lookup_validate, + .d_delete = smb_delete_dentry, +}; + + +/* + * This is the callback when the dcache has a lookup hit. + */ +static int +smb_lookup_validate(struct dentry * dentry, struct nameidata *nd) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + struct inode * inode = dentry->d_inode; + unsigned long age = jiffies - dentry->d_time; + int valid; + + /* + * The default validation is based on dentry age: + * we believe in dentries for a few seconds. (But each + * successful server lookup renews the timestamp.) + */ + valid = (age <= SMB_MAX_AGE(server)); +#ifdef SMBFS_DEBUG_VERBOSE + if (!valid) + VERBOSE("%s/%s not valid, age=%lu\n", + DENTRY_PATH(dentry), age); +#endif + + if (inode) { + lock_kernel(); + if (is_bad_inode(inode)) { + PARANOIA("%s/%s has dud inode\n", DENTRY_PATH(dentry)); + valid = 0; + } else if (!valid) + valid = (smb_revalidate_inode(dentry) == 0); + unlock_kernel(); + } else { + /* + * What should we do for negative dentries? + */ + } + return valid; +} + +static int +smb_hash_dentry(struct dentry *dir, struct qstr *this) +{ + unsigned long hash; + int i; + + hash = init_name_hash(); + for (i=0; i < this->len ; i++) + hash = partial_name_hash(tolower(this->name[i]), hash); + this->hash = end_name_hash(hash); + + return 0; +} + +static int +smb_compare_dentry(struct dentry *dir, struct qstr *a, struct qstr *b) +{ + int i, result = 1; + + if (a->len != b->len) + goto out; + for (i=0; i < a->len; i++) { + if (tolower(a->name[i]) != tolower(b->name[i])) + goto out; + } + result = 0; +out: + return result; +} + +/* + * This is the callback from dput() when d_count is going to 0. + * We use this to unhash dentries with bad inodes. + */ +static int +smb_delete_dentry(struct dentry * dentry) +{ + if (dentry->d_inode) { + if (is_bad_inode(dentry->d_inode)) { + PARANOIA("bad inode, unhashing %s/%s\n", + DENTRY_PATH(dentry)); + return 1; + } + } else { + /* N.B. Unhash negative dentries? */ + } + return 0; +} + +/* + * Initialize a new dentry + */ +void +smb_new_dentry(struct dentry *dentry) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + + if (server->mnt->flags & SMB_MOUNT_CASE) + dentry->d_op = &smbfs_dentry_operations_case; + else + dentry->d_op = &smbfs_dentry_operations; + dentry->d_time = jiffies; +} + + +/* + * Whenever a lookup succeeds, we know the parent directories + * are all valid, so we want to update the dentry timestamps. + * N.B. Move this to dcache? + */ +void +smb_renew_times(struct dentry * dentry) +{ + dget(dentry); + spin_lock(&dentry->d_lock); + for (;;) { + struct dentry *parent; + + dentry->d_time = jiffies; + if (IS_ROOT(dentry)) + break; + parent = dentry->d_parent; + dget(parent); + spin_unlock(&dentry->d_lock); + dput(dentry); + dentry = parent; + spin_lock(&dentry->d_lock); + } + spin_unlock(&dentry->d_lock); + dput(dentry); +} + +static struct dentry * +smb_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + struct smb_fattr finfo; + struct inode *inode; + int error; + struct smb_sb_info *server; + + error = -ENAMETOOLONG; + if (dentry->d_name.len > SMB_MAXNAMELEN) + goto out; + + /* Do not allow lookup of names with backslashes in */ + error = -EINVAL; + if (memchr(dentry->d_name.name, '\\', dentry->d_name.len)) + goto out; + + lock_kernel(); + error = smb_proc_getattr(dentry, &finfo); +#ifdef SMBFS_PARANOIA + if (error && error != -ENOENT) + PARANOIA("find %s/%s failed, error=%d\n", + DENTRY_PATH(dentry), error); +#endif + + inode = NULL; + if (error == -ENOENT) + goto add_entry; + if (!error) { + error = -EACCES; + finfo.f_ino = iunique(dentry->d_sb, 2); + inode = smb_iget(dir->i_sb, &finfo); + if (inode) { + add_entry: + server = server_from_dentry(dentry); + if (server->mnt->flags & SMB_MOUNT_CASE) + dentry->d_op = &smbfs_dentry_operations_case; + else + dentry->d_op = &smbfs_dentry_operations; + + d_add(dentry, inode); + smb_renew_times(dentry); + error = 0; + } + } + unlock_kernel(); +out: + return ERR_PTR(error); +} + +/* + * This code is common to all routines creating a new inode. + */ +static int +smb_instantiate(struct dentry *dentry, __u16 fileid, int have_id) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + struct inode *inode; + int error; + struct smb_fattr fattr; + + VERBOSE("file %s/%s, fileid=%u\n", DENTRY_PATH(dentry), fileid); + + error = smb_proc_getattr(dentry, &fattr); + if (error) + goto out_close; + + smb_renew_times(dentry); + fattr.f_ino = iunique(dentry->d_sb, 2); + inode = smb_iget(dentry->d_sb, &fattr); + if (!inode) + goto out_no_inode; + + if (have_id) { + struct smb_inode_info *ei = SMB_I(inode); + ei->fileid = fileid; + ei->access = SMB_O_RDWR; + ei->open = server->generation; + } + d_instantiate(dentry, inode); +out: + return error; + +out_no_inode: + error = -EACCES; +out_close: + if (have_id) { + PARANOIA("%s/%s failed, error=%d, closing %u\n", + DENTRY_PATH(dentry), error, fileid); + smb_close_fileid(dentry, fileid); + } + goto out; +} + +/* N.B. How should the mode argument be used? */ +static int +smb_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + __u16 fileid; + int error; + struct iattr attr; + + VERBOSE("creating %s/%s, mode=%d\n", DENTRY_PATH(dentry), mode); + + lock_kernel(); + smb_invalid_dir_cache(dir); + error = smb_proc_create(dentry, 0, get_seconds(), &fileid); + if (!error) { + if (server->opt.capabilities & SMB_CAP_UNIX) { + /* Set attributes for new file */ + attr.ia_valid = ATTR_MODE; + attr.ia_mode = mode; + error = smb_proc_setattr_unix(dentry, &attr, 0, 0); + } + error = smb_instantiate(dentry, fileid, 1); + } else { + PARANOIA("%s/%s failed, error=%d\n", + DENTRY_PATH(dentry), error); + } + unlock_kernel(); + return error; +} + +/* N.B. How should the mode argument be used? */ +static int +smb_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + int error; + struct iattr attr; + + lock_kernel(); + smb_invalid_dir_cache(dir); + error = smb_proc_mkdir(dentry); + if (!error) { + if (server->opt.capabilities & SMB_CAP_UNIX) { + /* Set attributes for new directory */ + attr.ia_valid = ATTR_MODE; + attr.ia_mode = mode; + error = smb_proc_setattr_unix(dentry, &attr, 0, 0); + } + error = smb_instantiate(dentry, 0, 0); + } + unlock_kernel(); + return error; +} + +static int +smb_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + int error; + + /* + * Close the directory if it's open. + */ + lock_kernel(); + smb_close(inode); + + /* + * Check that nobody else is using the directory.. + */ + error = -EBUSY; + if (!d_unhashed(dentry)) + goto out; + + smb_invalid_dir_cache(dir); + error = smb_proc_rmdir(dentry); + +out: + unlock_kernel(); + return error; +} + +static int +smb_unlink(struct inode *dir, struct dentry *dentry) +{ + int error; + + /* + * Close the file if it's open. + */ + lock_kernel(); + smb_close(dentry->d_inode); + + smb_invalid_dir_cache(dir); + error = smb_proc_unlink(dentry); + if (!error) + smb_renew_times(dentry); + unlock_kernel(); + return error; +} + +static int +smb_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + int error; + + /* + * Close any open files, and check whether to delete the + * target before attempting the rename. + */ + lock_kernel(); + if (old_dentry->d_inode) + smb_close(old_dentry->d_inode); + if (new_dentry->d_inode) { + smb_close(new_dentry->d_inode); + error = smb_proc_unlink(new_dentry); + if (error) { + VERBOSE("unlink %s/%s, error=%d\n", + DENTRY_PATH(new_dentry), error); + goto out; + } + /* FIXME */ + d_delete(new_dentry); + } + + smb_invalid_dir_cache(old_dir); + smb_invalid_dir_cache(new_dir); + error = smb_proc_mv(old_dentry, new_dentry); + if (!error) { + smb_renew_times(old_dentry); + smb_renew_times(new_dentry); + } +out: + unlock_kernel(); + return error; +} + +/* + * FIXME: samba servers won't let you create device nodes unless uid/gid + * matches the connection credentials (and we don't know which those are ...) + */ +static int +smb_make_node(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) +{ + int error; + struct iattr attr; + + attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID; + attr.ia_mode = mode; + current_euid_egid(&attr.ia_uid, &attr.ia_gid); + + if (!new_valid_dev(dev)) + return -EINVAL; + + smb_invalid_dir_cache(dir); + error = smb_proc_setattr_unix(dentry, &attr, MAJOR(dev), MINOR(dev)); + if (!error) { + error = smb_instantiate(dentry, 0, 0); + } + return error; +} + +/* + * dentry = existing file + * new_dentry = new file + */ +static int +smb_link(struct dentry *dentry, struct inode *dir, struct dentry *new_dentry) +{ + int error; + + DEBUG1("smb_link old=%s/%s new=%s/%s\n", + DENTRY_PATH(dentry), DENTRY_PATH(new_dentry)); + smb_invalid_dir_cache(dir); + error = smb_proc_link(server_from_dentry(dentry), dentry, new_dentry); + if (!error) { + smb_renew_times(dentry); + error = smb_instantiate(new_dentry, 0, 0); + } + return error; +} diff --git a/drivers/staging/smbfs/file.c b/drivers/staging/smbfs/file.c new file mode 100644 index 000000000000..5dcd19c60eb9 --- /dev/null +++ b/drivers/staging/smbfs/file.c @@ -0,0 +1,453 @@ +/* + * file.c + * + * Copyright (C) 1995, 1996, 1997 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "smbno.h" +#include "smb_fs.h" +#include "smb_debug.h" +#include "proto.h" + +static int +smb_fsync(struct file *file, int datasync) +{ + struct dentry *dentry = file->f_path.dentry; + struct smb_sb_info *server = server_from_dentry(dentry); + int result; + + VERBOSE("sync file %s/%s\n", DENTRY_PATH(dentry)); + + /* + * The VFS will writepage() all dirty pages for us, but we + * should send a SMBflush to the server, letting it know that + * we want things synchronized with actual storage. + * + * Note: this function requires all pages to have been written already + * (should be ok with writepage_sync) + */ + result = smb_proc_flush(server, SMB_I(dentry->d_inode)->fileid); + return result; +} + +/* + * Read a page synchronously. + */ +static int +smb_readpage_sync(struct dentry *dentry, struct page *page) +{ + char *buffer = kmap(page); + loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT; + struct smb_sb_info *server = server_from_dentry(dentry); + unsigned int rsize = smb_get_rsize(server); + int count = PAGE_SIZE; + int result; + + VERBOSE("file %s/%s, count=%d@%Ld, rsize=%d\n", + DENTRY_PATH(dentry), count, offset, rsize); + + result = smb_open(dentry, SMB_O_RDONLY); + if (result < 0) + goto io_error; + + do { + if (count < rsize) + rsize = count; + + result = server->ops->read(dentry->d_inode,offset,rsize,buffer); + if (result < 0) + goto io_error; + + count -= result; + offset += result; + buffer += result; + dentry->d_inode->i_atime = + current_fs_time(dentry->d_inode->i_sb); + if (result < rsize) + break; + } while (count); + + memset(buffer, 0, count); + flush_dcache_page(page); + SetPageUptodate(page); + result = 0; + +io_error: + kunmap(page); + unlock_page(page); + return result; +} + +/* + * We are called with the page locked and we unlock it when done. + */ +static int +smb_readpage(struct file *file, struct page *page) +{ + int error; + struct dentry *dentry = file->f_path.dentry; + + page_cache_get(page); + error = smb_readpage_sync(dentry, page); + page_cache_release(page); + return error; +} + +/* + * Write a page synchronously. + * Offset is the data offset within the page. + */ +static int +smb_writepage_sync(struct inode *inode, struct page *page, + unsigned long pageoffset, unsigned int count) +{ + loff_t offset; + char *buffer = kmap(page) + pageoffset; + struct smb_sb_info *server = server_from_inode(inode); + unsigned int wsize = smb_get_wsize(server); + int ret = 0; + + offset = ((loff_t)page->index << PAGE_CACHE_SHIFT) + pageoffset; + VERBOSE("file ino=%ld, fileid=%d, count=%d@%Ld, wsize=%d\n", + inode->i_ino, SMB_I(inode)->fileid, count, offset, wsize); + + do { + int write_ret; + + if (count < wsize) + wsize = count; + + write_ret = server->ops->write(inode, offset, wsize, buffer); + if (write_ret < 0) { + PARANOIA("failed write, wsize=%d, write_ret=%d\n", + wsize, write_ret); + ret = write_ret; + break; + } + /* N.B. what if result < wsize?? */ +#ifdef SMBFS_PARANOIA + if (write_ret < wsize) + PARANOIA("short write, wsize=%d, write_ret=%d\n", + wsize, write_ret); +#endif + buffer += wsize; + offset += wsize; + count -= wsize; + /* + * Update the inode now rather than waiting for a refresh. + */ + inode->i_mtime = inode->i_atime = current_fs_time(inode->i_sb); + SMB_I(inode)->flags |= SMB_F_LOCALWRITE; + if (offset > inode->i_size) + inode->i_size = offset; + } while (count); + + kunmap(page); + return ret; +} + +/* + * Write a page to the server. This will be used for NFS swapping only + * (for now), and we currently do this synchronously only. + * + * We are called with the page locked and we unlock it when done. + */ +static int +smb_writepage(struct page *page, struct writeback_control *wbc) +{ + struct address_space *mapping = page->mapping; + struct inode *inode; + unsigned long end_index; + unsigned offset = PAGE_CACHE_SIZE; + int err; + + BUG_ON(!mapping); + inode = mapping->host; + BUG_ON(!inode); + + end_index = inode->i_size >> PAGE_CACHE_SHIFT; + + /* easy case */ + if (page->index < end_index) + goto do_it; + /* things got complicated... */ + offset = inode->i_size & (PAGE_CACHE_SIZE-1); + /* OK, are we completely out? */ + if (page->index >= end_index+1 || !offset) + return 0; /* truncated - don't care */ +do_it: + page_cache_get(page); + err = smb_writepage_sync(inode, page, 0, offset); + SetPageUptodate(page); + unlock_page(page); + page_cache_release(page); + return err; +} + +static int +smb_updatepage(struct file *file, struct page *page, unsigned long offset, + unsigned int count) +{ + struct dentry *dentry = file->f_path.dentry; + + DEBUG1("(%s/%s %d@%lld)\n", DENTRY_PATH(dentry), count, + ((unsigned long long)page->index << PAGE_CACHE_SHIFT) + offset); + + return smb_writepage_sync(dentry->d_inode, page, offset, count); +} + +static ssize_t +smb_file_aio_read(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + struct file * file = iocb->ki_filp; + struct dentry * dentry = file->f_path.dentry; + ssize_t status; + + VERBOSE("file %s/%s, count=%lu@%lu\n", DENTRY_PATH(dentry), + (unsigned long) iocb->ki_left, (unsigned long) pos); + + status = smb_revalidate_inode(dentry); + if (status) { + PARANOIA("%s/%s validation failed, error=%Zd\n", + DENTRY_PATH(dentry), status); + goto out; + } + + VERBOSE("before read, size=%ld, flags=%x, atime=%ld\n", + (long)dentry->d_inode->i_size, + dentry->d_inode->i_flags, dentry->d_inode->i_atime.tv_sec); + + status = generic_file_aio_read(iocb, iov, nr_segs, pos); +out: + return status; +} + +static int +smb_file_mmap(struct file * file, struct vm_area_struct * vma) +{ + struct dentry * dentry = file->f_path.dentry; + int status; + + VERBOSE("file %s/%s, address %lu - %lu\n", + DENTRY_PATH(dentry), vma->vm_start, vma->vm_end); + + status = smb_revalidate_inode(dentry); + if (status) { + PARANOIA("%s/%s validation failed, error=%d\n", + DENTRY_PATH(dentry), status); + goto out; + } + status = generic_file_mmap(file, vma); +out: + return status; +} + +static ssize_t +smb_file_splice_read(struct file *file, loff_t *ppos, + struct pipe_inode_info *pipe, size_t count, + unsigned int flags) +{ + struct dentry *dentry = file->f_path.dentry; + ssize_t status; + + VERBOSE("file %s/%s, pos=%Ld, count=%lu\n", + DENTRY_PATH(dentry), *ppos, count); + + status = smb_revalidate_inode(dentry); + if (status) { + PARANOIA("%s/%s validation failed, error=%Zd\n", + DENTRY_PATH(dentry), status); + goto out; + } + status = generic_file_splice_read(file, ppos, pipe, count, flags); +out: + return status; +} + +/* + * This does the "real" work of the write. The generic routine has + * allocated the page, locked it, done all the page alignment stuff + * calculations etc. Now we should just copy the data from user + * space and write it back to the real medium.. + * + * If the writer ends up delaying the write, the writer needs to + * increment the page use counts until he is done with the page. + */ +static int smb_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ + pgoff_t index = pos >> PAGE_CACHE_SHIFT; + *pagep = grab_cache_page_write_begin(mapping, index, flags); + if (!*pagep) + return -ENOMEM; + return 0; +} + +static int smb_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ + int status; + unsigned offset = pos & (PAGE_CACHE_SIZE - 1); + + lock_kernel(); + status = smb_updatepage(file, page, offset, copied); + unlock_kernel(); + + if (!status) { + if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE) + SetPageUptodate(page); + status = copied; + } + + unlock_page(page); + page_cache_release(page); + + return status; +} + +const struct address_space_operations smb_file_aops = { + .readpage = smb_readpage, + .writepage = smb_writepage, + .write_begin = smb_write_begin, + .write_end = smb_write_end, +}; + +/* + * Write to a file (through the page cache). + */ +static ssize_t +smb_file_aio_write(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + struct file * file = iocb->ki_filp; + struct dentry * dentry = file->f_path.dentry; + ssize_t result; + + VERBOSE("file %s/%s, count=%lu@%lu\n", + DENTRY_PATH(dentry), + (unsigned long) iocb->ki_left, (unsigned long) pos); + + result = smb_revalidate_inode(dentry); + if (result) { + PARANOIA("%s/%s validation failed, error=%Zd\n", + DENTRY_PATH(dentry), result); + goto out; + } + + result = smb_open(dentry, SMB_O_WRONLY); + if (result) + goto out; + + if (iocb->ki_left > 0) { + result = generic_file_aio_write(iocb, iov, nr_segs, pos); + VERBOSE("pos=%ld, size=%ld, mtime=%ld, atime=%ld\n", + (long) file->f_pos, (long) dentry->d_inode->i_size, + dentry->d_inode->i_mtime.tv_sec, + dentry->d_inode->i_atime.tv_sec); + } +out: + return result; +} + +static int +smb_file_open(struct inode *inode, struct file * file) +{ + int result; + struct dentry *dentry = file->f_path.dentry; + int smb_mode = (file->f_mode & O_ACCMODE) - 1; + + lock_kernel(); + result = smb_open(dentry, smb_mode); + if (result) + goto out; + SMB_I(inode)->openers++; +out: + unlock_kernel(); + return result; +} + +static int +smb_file_release(struct inode *inode, struct file * file) +{ + lock_kernel(); + if (!--SMB_I(inode)->openers) { + /* We must flush any dirty pages now as we won't be able to + write anything after close. mmap can trigger this. + "openers" should perhaps include mmap'ers ... */ + filemap_write_and_wait(inode->i_mapping); + smb_close(inode); + } + unlock_kernel(); + return 0; +} + +/* + * Check whether the required access is compatible with + * an inode's permission. SMB doesn't recognize superuser + * privileges, so we need our own check for this. + */ +static int +smb_file_permission(struct inode *inode, int mask) +{ + int mode = inode->i_mode; + int error = 0; + + VERBOSE("mode=%x, mask=%x\n", mode, mask); + + /* Look at user permissions */ + mode >>= 6; + if (mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) + error = -EACCES; + return error; +} + +static loff_t smb_remote_llseek(struct file *file, loff_t offset, int origin) +{ + loff_t ret; + lock_kernel(); + ret = generic_file_llseek_unlocked(file, offset, origin); + unlock_kernel(); + return ret; +} + +const struct file_operations smb_file_operations = +{ + .llseek = smb_remote_llseek, + .read = do_sync_read, + .aio_read = smb_file_aio_read, + .write = do_sync_write, + .aio_write = smb_file_aio_write, + .unlocked_ioctl = smb_ioctl, + .mmap = smb_file_mmap, + .open = smb_file_open, + .release = smb_file_release, + .fsync = smb_fsync, + .splice_read = smb_file_splice_read, +}; + +const struct inode_operations smb_file_inode_operations = +{ + .permission = smb_file_permission, + .getattr = smb_getattr, + .setattr = smb_notify_change, +}; diff --git a/drivers/staging/smbfs/getopt.c b/drivers/staging/smbfs/getopt.c new file mode 100644 index 000000000000..7ae0f5273ab1 --- /dev/null +++ b/drivers/staging/smbfs/getopt.c @@ -0,0 +1,64 @@ +/* + * getopt.c + */ + +#include +#include +#include + +#include "getopt.h" + +/** + * smb_getopt - option parser + * @caller: name of the caller, for error messages + * @options: the options string + * @opts: an array of &struct option entries controlling parser operations + * @optopt: output; will contain the current option + * @optarg: output; will contain the value (if one exists) + * @flag: output; may be NULL; should point to a long for or'ing flags + * @value: output; may be NULL; will be overwritten with the integer value + * of the current argument. + * + * Helper to parse options on the format used by mount ("a=b,c=d,e,f"). + * Returns opts->val if a matching entry in the 'opts' array is found, + * 0 when no more tokens are found, -1 if an error is encountered. + */ +int smb_getopt(char *caller, char **options, struct option *opts, + char **optopt, char **optarg, unsigned long *flag, + unsigned long *value) +{ + char *token; + char *val; + int i; + + do { + if ((token = strsep(options, ",")) == NULL) + return 0; + } while (*token == '\0'); + *optopt = token; + + *optarg = NULL; + if ((val = strchr (token, '=')) != NULL) { + *val++ = 0; + if (value) + *value = simple_strtoul(val, NULL, 0); + *optarg = val; + } + + for (i = 0; opts[i].name != NULL; i++) { + if (!strcmp(opts[i].name, token)) { + if (!opts[i].flag && (!val || !*val)) { + printk("%s: the %s option requires an argument\n", + caller, token); + return -1; + } + + if (flag && opts[i].flag) + *flag |= opts[i].flag; + + return opts[i].val; + } + } + printk("%s: Unrecognized mount option %s\n", caller, token); + return -1; +} diff --git a/drivers/staging/smbfs/getopt.h b/drivers/staging/smbfs/getopt.h new file mode 100644 index 000000000000..146219ac7c46 --- /dev/null +++ b/drivers/staging/smbfs/getopt.h @@ -0,0 +1,14 @@ +#ifndef _LINUX_GETOPT_H +#define _LINUX_GETOPT_H + +struct option { + const char *name; + unsigned long flag; + int val; +}; + +extern int smb_getopt(char *caller, char **options, struct option *opts, + char **optopt, char **optarg, unsigned long *flag, + unsigned long *value); + +#endif /* _LINUX_GETOPT_H */ diff --git a/drivers/staging/smbfs/inode.c b/drivers/staging/smbfs/inode.c new file mode 100644 index 000000000000..9287599ddb50 --- /dev/null +++ b/drivers/staging/smbfs/inode.c @@ -0,0 +1,839 @@ +/* + * inode.c + * + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "smb_fs.h" +#include "smbno.h" +#include "smb_mount.h" +#include "smb_debug.h" +#include "getopt.h" +#include "proto.h" + +/* Always pick a default string */ +#ifdef CONFIG_SMB_NLS_REMOTE +#define SMB_NLS_REMOTE CONFIG_SMB_NLS_REMOTE +#else +#define SMB_NLS_REMOTE "" +#endif + +#define SMB_TTL_DEFAULT 1000 + +static void smb_evict_inode(struct inode *); +static void smb_put_super(struct super_block *); +static int smb_statfs(struct dentry *, struct kstatfs *); +static int smb_show_options(struct seq_file *, struct vfsmount *); + +static struct kmem_cache *smb_inode_cachep; + +static struct inode *smb_alloc_inode(struct super_block *sb) +{ + struct smb_inode_info *ei; + ei = (struct smb_inode_info *)kmem_cache_alloc(smb_inode_cachep, GFP_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void smb_destroy_inode(struct inode *inode) +{ + kmem_cache_free(smb_inode_cachep, SMB_I(inode)); +} + +static void init_once(void *foo) +{ + struct smb_inode_info *ei = (struct smb_inode_info *) foo; + + inode_init_once(&ei->vfs_inode); +} + +static int init_inodecache(void) +{ + smb_inode_cachep = kmem_cache_create("smb_inode_cache", + sizeof(struct smb_inode_info), + 0, (SLAB_RECLAIM_ACCOUNT| + SLAB_MEM_SPREAD), + init_once); + if (smb_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + kmem_cache_destroy(smb_inode_cachep); +} + +static int smb_remount(struct super_block *sb, int *flags, char *data) +{ + *flags |= MS_NODIRATIME; + return 0; +} + +static const struct super_operations smb_sops = +{ + .alloc_inode = smb_alloc_inode, + .destroy_inode = smb_destroy_inode, + .drop_inode = generic_delete_inode, + .evict_inode = smb_evict_inode, + .put_super = smb_put_super, + .statfs = smb_statfs, + .show_options = smb_show_options, + .remount_fs = smb_remount, +}; + + +/* We are always generating a new inode here */ +struct inode * +smb_iget(struct super_block *sb, struct smb_fattr *fattr) +{ + struct smb_sb_info *server = SMB_SB(sb); + struct inode *result; + + DEBUG1("smb_iget: %p\n", fattr); + + result = new_inode(sb); + if (!result) + return result; + result->i_ino = fattr->f_ino; + SMB_I(result)->open = 0; + SMB_I(result)->fileid = 0; + SMB_I(result)->access = 0; + SMB_I(result)->flags = 0; + SMB_I(result)->closed = 0; + SMB_I(result)->openers = 0; + smb_set_inode_attr(result, fattr); + if (S_ISREG(result->i_mode)) { + result->i_op = &smb_file_inode_operations; + result->i_fop = &smb_file_operations; + result->i_data.a_ops = &smb_file_aops; + } else if (S_ISDIR(result->i_mode)) { + if (server->opt.capabilities & SMB_CAP_UNIX) + result->i_op = &smb_dir_inode_operations_unix; + else + result->i_op = &smb_dir_inode_operations; + result->i_fop = &smb_dir_operations; + } else if (S_ISLNK(result->i_mode)) { + result->i_op = &smb_link_inode_operations; + } else { + init_special_inode(result, result->i_mode, fattr->f_rdev); + } + insert_inode_hash(result); + return result; +} + +/* + * Copy the inode data to a smb_fattr structure. + */ +void +smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr) +{ + memset(fattr, 0, sizeof(struct smb_fattr)); + fattr->f_mode = inode->i_mode; + fattr->f_nlink = inode->i_nlink; + fattr->f_ino = inode->i_ino; + fattr->f_uid = inode->i_uid; + fattr->f_gid = inode->i_gid; + fattr->f_size = inode->i_size; + fattr->f_mtime = inode->i_mtime; + fattr->f_ctime = inode->i_ctime; + fattr->f_atime = inode->i_atime; + fattr->f_blocks = inode->i_blocks; + + fattr->attr = SMB_I(inode)->attr; + /* + * Keep the attributes in sync with the inode permissions. + */ + if (fattr->f_mode & S_IWUSR) + fattr->attr &= ~aRONLY; + else + fattr->attr |= aRONLY; +} + +/* + * Update the inode, possibly causing it to invalidate its pages if mtime/size + * is different from last time. + */ +void +smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr) +{ + struct smb_inode_info *ei = SMB_I(inode); + + /* + * A size change should have a different mtime, or same mtime + * but different size. + */ + time_t last_time = inode->i_mtime.tv_sec; + loff_t last_sz = inode->i_size; + + inode->i_mode = fattr->f_mode; + inode->i_nlink = fattr->f_nlink; + inode->i_uid = fattr->f_uid; + inode->i_gid = fattr->f_gid; + inode->i_ctime = fattr->f_ctime; + inode->i_blocks = fattr->f_blocks; + inode->i_size = fattr->f_size; + inode->i_mtime = fattr->f_mtime; + inode->i_atime = fattr->f_atime; + ei->attr = fattr->attr; + + /* + * Update the "last time refreshed" field for revalidation. + */ + ei->oldmtime = jiffies; + + if (inode->i_mtime.tv_sec != last_time || inode->i_size != last_sz) { + VERBOSE("%ld changed, old=%ld, new=%ld, oz=%ld, nz=%ld\n", + inode->i_ino, + (long) last_time, (long) inode->i_mtime.tv_sec, + (long) last_sz, (long) inode->i_size); + + if (!S_ISDIR(inode->i_mode)) + invalidate_remote_inode(inode); + } +} + +/* + * This is called if the connection has gone bad ... + * try to kill off all the current inodes. + */ +void +smb_invalidate_inodes(struct smb_sb_info *server) +{ + VERBOSE("\n"); + shrink_dcache_sb(SB_of(server)); + invalidate_inodes(SB_of(server)); +} + +/* + * This is called to update the inode attributes after + * we've made changes to a file or directory. + */ +static int +smb_refresh_inode(struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + int error; + struct smb_fattr fattr; + + error = smb_proc_getattr(dentry, &fattr); + if (!error) { + smb_renew_times(dentry); + /* + * Check whether the type part of the mode changed, + * and don't update the attributes if it did. + * + * And don't dick with the root inode + */ + if (inode->i_ino == 2) + return error; + if (S_ISLNK(inode->i_mode)) + return error; /* VFS will deal with it */ + + if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) { + smb_set_inode_attr(inode, &fattr); + } else { + /* + * Big trouble! The inode has become a new object, + * so any operations attempted on it are invalid. + * + * To limit damage, mark the inode as bad so that + * subsequent lookup validations will fail. + */ + PARANOIA("%s/%s changed mode, %07o to %07o\n", + DENTRY_PATH(dentry), + inode->i_mode, fattr.f_mode); + + fattr.f_mode = inode->i_mode; /* save mode */ + make_bad_inode(inode); + inode->i_mode = fattr.f_mode; /* restore mode */ + /* + * No need to worry about unhashing the dentry: the + * lookup validation will see that the inode is bad. + * But we do want to invalidate the caches ... + */ + if (!S_ISDIR(inode->i_mode)) + invalidate_remote_inode(inode); + else + smb_invalid_dir_cache(inode); + error = -EIO; + } + } + return error; +} + +/* + * This is called when we want to check whether the inode + * has changed on the server. If it has changed, we must + * invalidate our local caches. + */ +int +smb_revalidate_inode(struct dentry *dentry) +{ + struct smb_sb_info *s = server_from_dentry(dentry); + struct inode *inode = dentry->d_inode; + int error = 0; + + DEBUG1("smb_revalidate_inode\n"); + lock_kernel(); + + /* + * Check whether we've recently refreshed the inode. + */ + if (time_before(jiffies, SMB_I(inode)->oldmtime + SMB_MAX_AGE(s))) { + VERBOSE("up-to-date, ino=%ld, jiffies=%lu, oldtime=%lu\n", + inode->i_ino, jiffies, SMB_I(inode)->oldmtime); + goto out; + } + + error = smb_refresh_inode(dentry); +out: + unlock_kernel(); + return error; +} + +/* + * This routine is called when i_nlink == 0 and i_count goes to 0. + * All blocking cleanup operations need to go here to avoid races. + */ +static void +smb_evict_inode(struct inode *ino) +{ + DEBUG1("ino=%ld\n", ino->i_ino); + truncate_inode_pages(&ino->i_data, 0); + end_writeback(ino); + lock_kernel(); + if (smb_close(ino)) + PARANOIA("could not close inode %ld\n", ino->i_ino); + unlock_kernel(); +} + +static struct option opts[] = { + { "version", 0, 'v' }, + { "win95", SMB_MOUNT_WIN95, 1 }, + { "oldattr", SMB_MOUNT_OLDATTR, 1 }, + { "dirattr", SMB_MOUNT_DIRATTR, 1 }, + { "case", SMB_MOUNT_CASE, 1 }, + { "uid", 0, 'u' }, + { "gid", 0, 'g' }, + { "file_mode", 0, 'f' }, + { "dir_mode", 0, 'd' }, + { "iocharset", 0, 'i' }, + { "codepage", 0, 'c' }, + { "ttl", 0, 't' }, + { NULL, 0, 0} +}; + +static int +parse_options(struct smb_mount_data_kernel *mnt, char *options) +{ + int c; + unsigned long flags; + unsigned long value; + char *optarg; + char *optopt; + + flags = 0; + while ( (c = smb_getopt("smbfs", &options, opts, + &optopt, &optarg, &flags, &value)) > 0) { + + VERBOSE("'%s' -> '%s'\n", optopt, optarg ? optarg : ""); + switch (c) { + case 1: + /* got a "flag" option */ + break; + case 'v': + if (value != SMB_MOUNT_VERSION) { + printk ("smbfs: Bad mount version %ld, expected %d\n", + value, SMB_MOUNT_VERSION); + return 0; + } + mnt->version = value; + break; + case 'u': + mnt->uid = value; + flags |= SMB_MOUNT_UID; + break; + case 'g': + mnt->gid = value; + flags |= SMB_MOUNT_GID; + break; + case 'f': + mnt->file_mode = (value & S_IRWXUGO) | S_IFREG; + flags |= SMB_MOUNT_FMODE; + break; + case 'd': + mnt->dir_mode = (value & S_IRWXUGO) | S_IFDIR; + flags |= SMB_MOUNT_DMODE; + break; + case 'i': + strlcpy(mnt->codepage.local_name, optarg, + SMB_NLS_MAXNAMELEN); + break; + case 'c': + strlcpy(mnt->codepage.remote_name, optarg, + SMB_NLS_MAXNAMELEN); + break; + case 't': + mnt->ttl = value; + break; + default: + printk ("smbfs: Unrecognized mount option %s\n", + optopt); + return -1; + } + } + mnt->flags = flags; + return c; +} + +/* + * smb_show_options() is for displaying mount options in /proc/mounts. + * It tries to avoid showing settings that were not changed from their + * defaults. + */ +static int +smb_show_options(struct seq_file *s, struct vfsmount *m) +{ + struct smb_mount_data_kernel *mnt = SMB_SB(m->mnt_sb)->mnt; + int i; + + for (i = 0; opts[i].name != NULL; i++) + if (mnt->flags & opts[i].flag) + seq_printf(s, ",%s", opts[i].name); + + if (mnt->flags & SMB_MOUNT_UID) + seq_printf(s, ",uid=%d", mnt->uid); + if (mnt->flags & SMB_MOUNT_GID) + seq_printf(s, ",gid=%d", mnt->gid); + if (mnt->mounted_uid != 0) + seq_printf(s, ",mounted_uid=%d", mnt->mounted_uid); + + /* + * Defaults for file_mode and dir_mode are unknown to us; they + * depend on the current umask of the user doing the mount. + */ + if (mnt->flags & SMB_MOUNT_FMODE) + seq_printf(s, ",file_mode=%04o", mnt->file_mode & S_IRWXUGO); + if (mnt->flags & SMB_MOUNT_DMODE) + seq_printf(s, ",dir_mode=%04o", mnt->dir_mode & S_IRWXUGO); + + if (strcmp(mnt->codepage.local_name, CONFIG_NLS_DEFAULT)) + seq_printf(s, ",iocharset=%s", mnt->codepage.local_name); + if (strcmp(mnt->codepage.remote_name, SMB_NLS_REMOTE)) + seq_printf(s, ",codepage=%s", mnt->codepage.remote_name); + + if (mnt->ttl != SMB_TTL_DEFAULT) + seq_printf(s, ",ttl=%d", mnt->ttl); + + return 0; +} + +static void +smb_unload_nls(struct smb_sb_info *server) +{ + unload_nls(server->remote_nls); + unload_nls(server->local_nls); +} + +static void +smb_put_super(struct super_block *sb) +{ + struct smb_sb_info *server = SMB_SB(sb); + + lock_kernel(); + + smb_lock_server(server); + server->state = CONN_INVALID; + smbiod_unregister_server(server); + + smb_close_socket(server); + + if (server->conn_pid) + kill_pid(server->conn_pid, SIGTERM, 1); + + bdi_destroy(&server->bdi); + kfree(server->ops); + smb_unload_nls(server); + sb->s_fs_info = NULL; + smb_unlock_server(server); + put_pid(server->conn_pid); + kfree(server); + + unlock_kernel(); +} + +static int smb_fill_super(struct super_block *sb, void *raw_data, int silent) +{ + struct smb_sb_info *server; + struct smb_mount_data_kernel *mnt; + struct smb_mount_data *oldmnt; + struct inode *root_inode; + struct smb_fattr root; + int ver; + void *mem; + static int warn_count; + + if (warn_count < 5) { + warn_count++; + printk(KERN_EMERG "smbfs is deprecated and will be removed" + " from the 2.6.37 kernel. Please migrate to cifs\n"); + } + + if (!raw_data) + goto out_no_data; + + oldmnt = (struct smb_mount_data *) raw_data; + ver = oldmnt->version; + if (ver != SMB_MOUNT_OLDVERSION && cpu_to_be32(ver) != SMB_MOUNT_ASCII) + goto out_wrong_data; + + sb->s_flags |= MS_NODIRATIME; + sb->s_blocksize = 1024; /* Eh... Is this correct? */ + sb->s_blocksize_bits = 10; + sb->s_magic = SMB_SUPER_MAGIC; + sb->s_op = &smb_sops; + sb->s_time_gran = 100; + + server = kzalloc(sizeof(struct smb_sb_info), GFP_KERNEL); + if (!server) + goto out_no_server; + sb->s_fs_info = server; + + if (bdi_setup_and_register(&server->bdi, "smbfs", BDI_CAP_MAP_COPY)) + goto out_bdi; + + sb->s_bdi = &server->bdi; + + server->super_block = sb; + server->mnt = NULL; + server->sock_file = NULL; + init_waitqueue_head(&server->conn_wq); + init_MUTEX(&server->sem); + INIT_LIST_HEAD(&server->entry); + INIT_LIST_HEAD(&server->xmitq); + INIT_LIST_HEAD(&server->recvq); + server->conn_error = 0; + server->conn_pid = NULL; + server->state = CONN_INVALID; /* no connection yet */ + server->generation = 0; + + /* Allocate the global temp buffer and some superblock helper structs */ + /* FIXME: move these to the smb_sb_info struct */ + VERBOSE("alloc chunk = %lu\n", sizeof(struct smb_ops) + + sizeof(struct smb_mount_data_kernel)); + mem = kmalloc(sizeof(struct smb_ops) + + sizeof(struct smb_mount_data_kernel), GFP_KERNEL); + if (!mem) + goto out_no_mem; + + server->ops = mem; + smb_install_null_ops(server->ops); + server->mnt = mem + sizeof(struct smb_ops); + + /* Setup NLS stuff */ + server->remote_nls = NULL; + server->local_nls = NULL; + + mnt = server->mnt; + + memset(mnt, 0, sizeof(struct smb_mount_data_kernel)); + strlcpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT, + SMB_NLS_MAXNAMELEN); + strlcpy(mnt->codepage.remote_name, SMB_NLS_REMOTE, + SMB_NLS_MAXNAMELEN); + + mnt->ttl = SMB_TTL_DEFAULT; + if (ver == SMB_MOUNT_OLDVERSION) { + mnt->version = oldmnt->version; + + SET_UID(mnt->uid, oldmnt->uid); + SET_GID(mnt->gid, oldmnt->gid); + + mnt->file_mode = (oldmnt->file_mode & S_IRWXUGO) | S_IFREG; + mnt->dir_mode = (oldmnt->dir_mode & S_IRWXUGO) | S_IFDIR; + + mnt->flags = (oldmnt->file_mode >> 9) | SMB_MOUNT_UID | + SMB_MOUNT_GID | SMB_MOUNT_FMODE | SMB_MOUNT_DMODE; + } else { + mnt->file_mode = S_IRWXU | S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH | S_IFREG; + mnt->dir_mode = S_IRWXU | S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH | S_IFDIR; + if (parse_options(mnt, raw_data)) + goto out_bad_option; + } + mnt->mounted_uid = current_uid(); + smb_setcodepage(server, &mnt->codepage); + + /* + * Display the enabled options + * Note: smb_proc_getattr uses these in 2.4 (but was changed in 2.2) + */ + if (mnt->flags & SMB_MOUNT_OLDATTR) + printk("SMBFS: Using core getattr (Win 95 speedup)\n"); + else if (mnt->flags & SMB_MOUNT_DIRATTR) + printk("SMBFS: Using dir ff getattr\n"); + + if (smbiod_register_server(server) < 0) { + printk(KERN_ERR "smbfs: failed to start smbiod\n"); + goto out_no_smbiod; + } + + /* + * Keep the super block locked while we get the root inode. + */ + smb_init_root_dirent(server, &root, sb); + root_inode = smb_iget(sb, &root); + if (!root_inode) + goto out_no_root; + + sb->s_root = d_alloc_root(root_inode); + if (!sb->s_root) + goto out_no_root; + + smb_new_dentry(sb->s_root); + + return 0; + +out_no_root: + iput(root_inode); +out_no_smbiod: + smb_unload_nls(server); +out_bad_option: + kfree(mem); +out_no_mem: + bdi_destroy(&server->bdi); +out_bdi: + if (!server->mnt) + printk(KERN_ERR "smb_fill_super: allocation failure\n"); + sb->s_fs_info = NULL; + kfree(server); + goto out_fail; +out_wrong_data: + printk(KERN_ERR "smbfs: mount_data version %d is not supported\n", ver); + goto out_fail; +out_no_data: + printk(KERN_ERR "smb_fill_super: missing data argument\n"); +out_fail: + return -EINVAL; +out_no_server: + printk(KERN_ERR "smb_fill_super: cannot allocate struct smb_sb_info\n"); + return -ENOMEM; +} + +static int +smb_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + int result; + + lock_kernel(); + + result = smb_proc_dskattr(dentry, buf); + + unlock_kernel(); + + buf->f_type = SMB_SUPER_MAGIC; + buf->f_namelen = SMB_MAXPATHLEN; + return result; +} + +int smb_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + int err = smb_revalidate_inode(dentry); + if (!err) + generic_fillattr(dentry->d_inode, stat); + return err; +} + +int +smb_notify_change(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + struct smb_sb_info *server = server_from_dentry(dentry); + unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXUGO); + int error, changed, refresh = 0; + struct smb_fattr fattr; + + lock_kernel(); + + error = smb_revalidate_inode(dentry); + if (error) + goto out; + + if ((error = inode_change_ok(inode, attr)) < 0) + goto out; + + error = -EPERM; + if ((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->mnt->uid)) + goto out; + + if ((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->mnt->gid)) + goto out; + + if ((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~mask)) + goto out; + + if ((attr->ia_valid & ATTR_SIZE) != 0) { + VERBOSE("changing %s/%s, old size=%ld, new size=%ld\n", + DENTRY_PATH(dentry), + (long) inode->i_size, (long) attr->ia_size); + + filemap_write_and_wait(inode->i_mapping); + + error = smb_open(dentry, O_WRONLY); + if (error) + goto out; + error = server->ops->truncate(inode, attr->ia_size); + if (error) + goto out; + truncate_setsize(inode, attr->ia_size); + refresh = 1; + } + + if (server->opt.capabilities & SMB_CAP_UNIX) { + /* For now we don't want to set the size with setattr_unix */ + attr->ia_valid &= ~ATTR_SIZE; + /* FIXME: only call if we actually want to set something? */ + error = smb_proc_setattr_unix(dentry, attr, 0, 0); + if (!error) + refresh = 1; + + goto out; + } + + /* + * Initialize the fattr and check for changed fields. + * Note: CTIME under SMB is creation time rather than + * change time, so we don't attempt to change it. + */ + smb_get_inode_attr(inode, &fattr); + + changed = 0; + if ((attr->ia_valid & ATTR_MTIME) != 0) { + fattr.f_mtime = attr->ia_mtime; + changed = 1; + } + if ((attr->ia_valid & ATTR_ATIME) != 0) { + fattr.f_atime = attr->ia_atime; + /* Earlier protocols don't have an access time */ + if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) + changed = 1; + } + if (changed) { + error = smb_proc_settime(dentry, &fattr); + if (error) + goto out; + refresh = 1; + } + + /* + * Check for mode changes ... we're extremely limited in + * what can be set for SMB servers: just the read-only bit. + */ + if ((attr->ia_valid & ATTR_MODE) != 0) { + VERBOSE("%s/%s mode change, old=%x, new=%x\n", + DENTRY_PATH(dentry), fattr.f_mode, attr->ia_mode); + changed = 0; + if (attr->ia_mode & S_IWUSR) { + if (fattr.attr & aRONLY) { + fattr.attr &= ~aRONLY; + changed = 1; + } + } else { + if (!(fattr.attr & aRONLY)) { + fattr.attr |= aRONLY; + changed = 1; + } + } + if (changed) { + error = smb_proc_setattr(dentry, &fattr); + if (error) + goto out; + refresh = 1; + } + } + error = 0; + +out: + if (refresh) + smb_refresh_inode(dentry); + unlock_kernel(); + return error; +} + +static int smb_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) +{ + return get_sb_nodev(fs_type, flags, data, smb_fill_super, mnt); +} + +static struct file_system_type smb_fs_type = { + .owner = THIS_MODULE, + .name = "smbfs", + .get_sb = smb_get_sb, + .kill_sb = kill_anon_super, + .fs_flags = FS_BINARY_MOUNTDATA, +}; + +static int __init init_smb_fs(void) +{ + int err; + DEBUG1("registering ...\n"); + + err = init_inodecache(); + if (err) + goto out_inode; + err = smb_init_request_cache(); + if (err) + goto out_request; + err = register_filesystem(&smb_fs_type); + if (err) + goto out; + return 0; +out: + smb_destroy_request_cache(); +out_request: + destroy_inodecache(); +out_inode: + return err; +} + +static void __exit exit_smb_fs(void) +{ + DEBUG1("unregistering ...\n"); + unregister_filesystem(&smb_fs_type); + smb_destroy_request_cache(); + destroy_inodecache(); +} + +module_init(init_smb_fs) +module_exit(exit_smb_fs) +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/smbfs/ioctl.c b/drivers/staging/smbfs/ioctl.c new file mode 100644 index 000000000000..2da169267470 --- /dev/null +++ b/drivers/staging/smbfs/ioctl.c @@ -0,0 +1,68 @@ +/* + * ioctl.c + * + * Copyright (C) 1995, 1996 by Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "smb_fs.h" +#include "smb_mount.h" +#include "proto.h" + +long +smb_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct smb_sb_info *server = server_from_inode(filp->f_path.dentry->d_inode); + struct smb_conn_opt opt; + int result = -EINVAL; + + lock_kernel(); + switch (cmd) { + uid16_t uid16; + uid_t uid32; + case SMB_IOC_GETMOUNTUID: + SET_UID(uid16, server->mnt->mounted_uid); + result = put_user(uid16, (uid16_t __user *) arg); + break; + case SMB_IOC_GETMOUNTUID32: + SET_UID(uid32, server->mnt->mounted_uid); + result = put_user(uid32, (uid_t __user *) arg); + break; + + case SMB_IOC_NEWCONN: + /* arg is smb_conn_opt, or NULL if no connection was made */ + if (!arg) { + result = 0; + smb_lock_server(server); + server->state = CONN_RETRIED; + printk(KERN_ERR "Connection attempt failed! [%d]\n", + server->conn_error); + smbiod_flush(server); + smb_unlock_server(server); + break; + } + + result = -EFAULT; + if (!copy_from_user(&opt, (void __user *)arg, sizeof(opt))) + result = smb_newconn(server, &opt); + break; + default: + break; + } + unlock_kernel(); + + return result; +} diff --git a/drivers/staging/smbfs/proc.c b/drivers/staging/smbfs/proc.c new file mode 100644 index 000000000000..2fb079c37f25 --- /dev/null +++ b/drivers/staging/smbfs/proc.c @@ -0,0 +1,3506 @@ +/* + * proc.c + * + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "smb_fs.h" +#include "smbno.h" +#include "smb_mount.h" +#include "smb_debug.h" +#include "proto.h" +#include "request.h" + + +/* Features. Undefine if they cause problems, this should perhaps be a + config option. */ +#define SMBFS_POSIX_UNLINK 1 + +/* Allow smb_retry to be interrupted. */ +#define SMB_RETRY_INTR + +#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN) +#define SMB_CMD(packet) (*(packet+8)) +#define SMB_WCT(packet) (*(packet+SMB_HEADER_LEN - 1)) + +#define SMB_DIRINFO_SIZE 43 +#define SMB_STATUS_SIZE 21 + +#define SMB_ST_BLKSIZE (PAGE_SIZE) +#define SMB_ST_BLKSHIFT (PAGE_SHIFT) + +static struct smb_ops smb_ops_core; +static struct smb_ops smb_ops_os2; +static struct smb_ops smb_ops_win95; +static struct smb_ops smb_ops_winNT; +static struct smb_ops smb_ops_unix; +static struct smb_ops smb_ops_null; + +static void +smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); +static void +smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); +static int +smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *fattr); +static int +smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, + struct smb_fattr *fattr); +static int +smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, + u16 attr); +static int +smb_proc_setattr_ext(struct smb_sb_info *server, + struct inode *inode, struct smb_fattr *fattr); +static int +smb_proc_query_cifsunix(struct smb_sb_info *server); +static void +install_ops(struct smb_ops *dst, struct smb_ops *src); + + +static void +str_upper(char *name, int len) +{ + while (len--) + { + if (*name >= 'a' && *name <= 'z') + *name -= ('a' - 'A'); + name++; + } +} + +#if 0 +static void +str_lower(char *name, int len) +{ + while (len--) + { + if (*name >= 'A' && *name <= 'Z') + *name += ('a' - 'A'); + name++; + } +} +#endif + +/* reverse a string inline. This is used by the dircache walking routines */ +static void reverse_string(char *buf, int len) +{ + char c; + char *end = buf+len-1; + + while(buf < end) { + c = *buf; + *(buf++) = *end; + *(end--) = c; + } +} + +/* no conversion, just a wrapper for memcpy. */ +static int convert_memcpy(unsigned char *output, int olen, + const unsigned char *input, int ilen, + struct nls_table *nls_from, + struct nls_table *nls_to) +{ + if (olen < ilen) + return -ENAMETOOLONG; + memcpy(output, input, ilen); + return ilen; +} + +static inline int write_char(unsigned char ch, char *output, int olen) +{ + if (olen < 4) + return -ENAMETOOLONG; + sprintf(output, ":x%02x", ch); + return 4; +} + +static inline int write_unichar(wchar_t ch, char *output, int olen) +{ + if (olen < 5) + return -ENAMETOOLONG; + sprintf(output, ":%04x", ch); + return 5; +} + +/* convert from one "codepage" to another (possibly being utf8). */ +static int convert_cp(unsigned char *output, int olen, + const unsigned char *input, int ilen, + struct nls_table *nls_from, + struct nls_table *nls_to) +{ + int len = 0; + int n; + wchar_t ch; + + while (ilen > 0) { + /* convert by changing to unicode and back to the new cp */ + n = nls_from->char2uni(input, ilen, &ch); + if (n == -EINVAL) { + ilen--; + n = write_char(*input++, output, olen); + if (n < 0) + goto fail; + output += n; + olen -= n; + len += n; + continue; + } else if (n < 0) + goto fail; + input += n; + ilen -= n; + + n = nls_to->uni2char(ch, output, olen); + if (n == -EINVAL) + n = write_unichar(ch, output, olen); + if (n < 0) + goto fail; + output += n; + olen -= n; + + len += n; + } + return len; +fail: + return n; +} + +/* ----------------------------------------------------------- */ + +/* + * nls_unicode + * + * This encodes/decodes little endian unicode format + */ + +static int uni2char(wchar_t uni, unsigned char *out, int boundlen) +{ + if (boundlen < 2) + return -EINVAL; + *out++ = uni & 0xff; + *out++ = uni >> 8; + return 2; +} + +static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni) +{ + if (boundlen < 2) + return -EINVAL; + *uni = (rawstring[1] << 8) | rawstring[0]; + return 2; +} + +static struct nls_table unicode_table = { + .charset = "unicode", + .uni2char = uni2char, + .char2uni = char2uni, +}; + +/* ----------------------------------------------------------- */ + +static int setcodepage(struct nls_table **p, char *name) +{ + struct nls_table *nls; + + if (!name || !*name) { + nls = NULL; + } else if ( (nls = load_nls(name)) == NULL) { + printk (KERN_ERR "smbfs: failed to load nls '%s'\n", name); + return -EINVAL; + } + + /* if already set, unload the previous one. */ + if (*p && *p != &unicode_table) + unload_nls(*p); + *p = nls; + + return 0; +} + +/* Handles all changes to codepage settings. */ +int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp) +{ + int n = 0; + + smb_lock_server(server); + + /* Don't load any nls_* at all, if no remote is requested */ + if (!*cp->remote_name) + goto out; + + /* local */ + n = setcodepage(&server->local_nls, cp->local_name); + if (n != 0) + goto out; + + /* remote */ + if (!strcmp(cp->remote_name, "unicode")) { + server->remote_nls = &unicode_table; + } else { + n = setcodepage(&server->remote_nls, cp->remote_name); + if (n != 0) + setcodepage(&server->local_nls, NULL); + } + +out: + if (server->local_nls != NULL && server->remote_nls != NULL) + server->ops->convert = convert_cp; + else + server->ops->convert = convert_memcpy; + + smb_unlock_server(server); + return n; +} + + +/*****************************************************************************/ +/* */ +/* Encoding/Decoding section */ +/* */ +/*****************************************************************************/ + +static __u8 * +smb_encode_smb_length(__u8 * p, __u32 len) +{ + *p = 0; + *(p+1) = 0; + *(p+2) = (len & 0xFF00) >> 8; + *(p+3) = (len & 0xFF); + if (len > 0xFFFF) + { + *(p+1) = 1; + } + return p + 4; +} + +/* + * smb_build_path: build the path to entry and name storing it in buf. + * The path returned will have the trailing '\0'. + */ +static int smb_build_path(struct smb_sb_info *server, unsigned char *buf, + int maxlen, + struct dentry *entry, struct qstr *name) +{ + unsigned char *path = buf; + int len; + int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE) != 0; + + if (maxlen < (2< SMB_MAXPATHLEN + 1) + maxlen = SMB_MAXPATHLEN + 1; + + if (entry == NULL) + goto test_name_and_out; + + /* + * If IS_ROOT, we have to do no walking at all. + */ + if (IS_ROOT(entry) && !name) { + *path++ = '\\'; + if (unicode) *path++ = '\0'; + *path++ = '\0'; + if (unicode) *path++ = '\0'; + return path-buf; + } + + /* + * Build the path string walking the tree backward from end to ROOT + * and store it in reversed order [see reverse_string()] + */ + dget(entry); + spin_lock(&entry->d_lock); + while (!IS_ROOT(entry)) { + struct dentry *parent; + + if (maxlen < (3<d_lock); + dput(entry); + return -ENAMETOOLONG; + } + + len = server->ops->convert(path, maxlen-2, + entry->d_name.name, entry->d_name.len, + server->local_nls, server->remote_nls); + if (len < 0) { + spin_unlock(&entry->d_lock); + dput(entry); + return len; + } + reverse_string(path, len); + path += len; + if (unicode) { + /* Note: reverse order */ + *path++ = '\0'; + maxlen--; + } + *path++ = '\\'; + maxlen -= len+1; + + parent = entry->d_parent; + dget(parent); + spin_unlock(&entry->d_lock); + dput(entry); + entry = parent; + spin_lock(&entry->d_lock); + } + spin_unlock(&entry->d_lock); + dput(entry); + reverse_string(buf, path-buf); + + /* maxlen has space for at least one char */ +test_name_and_out: + if (name) { + if (maxlen < (3<ops->convert(path, maxlen-2, + name->name, name->len, + server->local_nls, server->remote_nls); + if (len < 0) + return len; + path += len; + maxlen -= len+1; + } + /* maxlen has space for at least one char */ + *path++ = '\0'; + if (unicode) *path++ = '\0'; + return path-buf; +} + +static int smb_encode_path(struct smb_sb_info *server, char *buf, int maxlen, + struct dentry *dir, struct qstr *name) +{ + int result; + + result = smb_build_path(server, buf, maxlen, dir, name); + if (result < 0) + goto out; + if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS) + str_upper(buf, result); +out: + return result; +} + +/* encode_path for non-trans2 request SMBs */ +static int smb_simple_encode_path(struct smb_request *req, char **p, + struct dentry * entry, struct qstr * name) +{ + struct smb_sb_info *server = req->rq_server; + char *s = *p; + int res; + int maxlen = ((char *)req->rq_buffer + req->rq_bufsize) - s; + int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE); + + if (!maxlen) + return -ENAMETOOLONG; + *s++ = 4; /* ASCII data format */ + + /* + * SMB Unicode strings must be 16bit aligned relative the start of the + * packet. If they are not they must be padded with 0. + */ + if (unicode) { + int align = s - (char *)req->rq_buffer; + if (!(align & 1)) { + *s++ = '\0'; + maxlen--; + } + } + + res = smb_encode_path(server, s, maxlen-1, entry, name); + if (res < 0) + return res; + *p = s + res; + return 0; +} + +/* The following are taken directly from msdos-fs */ + +/* Linear day numbers of the respective 1sts in non-leap years. */ + +static int day_n[] = +{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0}; + /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ + + +static time_t +utc2local(struct smb_sb_info *server, time_t time) +{ + return time - server->opt.serverzone*60; +} + +static time_t +local2utc(struct smb_sb_info *server, time_t time) +{ + return time + server->opt.serverzone*60; +} + +/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ + +static time_t +date_dos2unix(struct smb_sb_info *server, __u16 date, __u16 time) +{ + int month, year; + time_t secs; + + /* first subtract and mask after that... Otherwise, if + date == 0, bad things happen */ + month = ((date >> 5) - 1) & 15; + year = date >> 9; + secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 * + ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 && + month < 2 ? 1 : 0) + 3653); + /* days since 1.1.70 plus 80's leap day */ + return local2utc(server, secs); +} + + +/* Convert linear UNIX date to a MS-DOS time/date pair. */ + +static void +date_unix2dos(struct smb_sb_info *server, + int unix_date, __u16 *date, __u16 *time) +{ + int day, year, nl_day, month; + + unix_date = utc2local(server, unix_date); + if (unix_date < 315532800) + unix_date = 315532800; + + *time = (unix_date % 60) / 2 + + (((unix_date / 60) % 60) << 5) + + (((unix_date / 3600) % 24) << 11); + + day = unix_date / 86400 - 3652; + year = day / 365; + if ((year + 3) / 4 + 365 * year > day) + year--; + day -= (year + 3) / 4 + 365 * year; + if (day == 59 && !(year & 3)) { + nl_day = day; + month = 2; + } else { + nl_day = (year & 3) || day <= 59 ? day : day - 1; + for (month = 1; month < 12; month++) + if (day_n[month] > nl_day) + break; + } + *date = nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9); +} + +/* The following are taken from fs/ntfs/util.c */ + +#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000) + +/* + * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) + * into Unix UTC (based 1970-01-01, in seconds). + */ +static struct timespec +smb_ntutc2unixutc(u64 ntutc) +{ + struct timespec ts; + /* FIXME: what about the timezone difference? */ + /* Subtract the NTFS time offset, then convert to 1s intervals. */ + u64 t = ntutc - NTFS_TIME_OFFSET; + ts.tv_nsec = do_div(t, 10000000) * 100; + ts.tv_sec = t; + return ts; +} + +/* Convert the Unix UTC into NT time */ +static u64 +smb_unixutc2ntutc(struct timespec ts) +{ + /* Note: timezone conversion is probably wrong. */ + /* return ((u64)utc2local(server, t)) * 10000000 + NTFS_TIME_OFFSET; */ + return ((u64)ts.tv_sec) * 10000000 + ts.tv_nsec/100 + NTFS_TIME_OFFSET; +} + +#define MAX_FILE_MODE 6 +static mode_t file_mode[] = { + S_IFREG, S_IFDIR, S_IFLNK, S_IFCHR, S_IFBLK, S_IFIFO, S_IFSOCK +}; + +static int smb_filetype_to_mode(u32 filetype) +{ + if (filetype > MAX_FILE_MODE) { + PARANOIA("Filetype out of range: %d\n", filetype); + return S_IFREG; + } + return file_mode[filetype]; +} + +static u32 smb_filetype_from_mode(int mode) +{ + if (S_ISREG(mode)) + return UNIX_TYPE_FILE; + if (S_ISDIR(mode)) + return UNIX_TYPE_DIR; + if (S_ISLNK(mode)) + return UNIX_TYPE_SYMLINK; + if (S_ISCHR(mode)) + return UNIX_TYPE_CHARDEV; + if (S_ISBLK(mode)) + return UNIX_TYPE_BLKDEV; + if (S_ISFIFO(mode)) + return UNIX_TYPE_FIFO; + if (S_ISSOCK(mode)) + return UNIX_TYPE_SOCKET; + return UNIX_TYPE_UNKNOWN; +} + + +/*****************************************************************************/ +/* */ +/* Support section. */ +/* */ +/*****************************************************************************/ + +__u32 +smb_len(__u8 * p) +{ + return ((*(p+1) & 0x1) << 16L) | (*(p+2) << 8L) | *(p+3); +} + +static __u16 +smb_bcc(__u8 * packet) +{ + int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(__u16); + return WVAL(packet, pos); +} + +/* smb_valid_packet: We check if packet fulfills the basic + requirements of a smb packet */ + +static int +smb_valid_packet(__u8 * packet) +{ + return (packet[4] == 0xff + && packet[5] == 'S' + && packet[6] == 'M' + && packet[7] == 'B' + && (smb_len(packet) + 4 == SMB_HEADER_LEN + + SMB_WCT(packet) * 2 + smb_bcc(packet))); +} + +/* smb_verify: We check if we got the answer we expected, and if we + got enough data. If bcc == -1, we don't care. */ + +static int +smb_verify(__u8 * packet, int command, int wct, int bcc) +{ + if (SMB_CMD(packet) != command) + goto bad_command; + if (SMB_WCT(packet) < wct) + goto bad_wct; + if (bcc != -1 && smb_bcc(packet) < bcc) + goto bad_bcc; + return 0; + +bad_command: + printk(KERN_ERR "smb_verify: command=%x, SMB_CMD=%x??\n", + command, SMB_CMD(packet)); + goto fail; +bad_wct: + printk(KERN_ERR "smb_verify: command=%x, wct=%d, SMB_WCT=%d??\n", + command, wct, SMB_WCT(packet)); + goto fail; +bad_bcc: + printk(KERN_ERR "smb_verify: command=%x, bcc=%d, SMB_BCC=%d??\n", + command, bcc, smb_bcc(packet)); +fail: + return -EIO; +} + +/* + * Returns the maximum read or write size for the "payload". Making all of the + * packet fit within the negotiated max_xmit size. + * + * N.B. Since this value is usually computed before locking the server, + * the server's packet size must never be decreased! + */ +static inline int +smb_get_xmitsize(struct smb_sb_info *server, int overhead) +{ + return server->opt.max_xmit - overhead; +} + +/* + * Calculate the maximum read size + */ +int +smb_get_rsize(struct smb_sb_info *server) +{ + /* readX has 12 parameters, read has 5 */ + int overhead = SMB_HEADER_LEN + 12 * sizeof(__u16) + 2 + 1 + 2; + int size = smb_get_xmitsize(server, overhead); + + VERBOSE("xmit=%d, size=%d\n", server->opt.max_xmit, size); + + return size; +} + +/* + * Calculate the maximum write size + */ +int +smb_get_wsize(struct smb_sb_info *server) +{ + /* writeX has 14 parameters, write has 5 */ + int overhead = SMB_HEADER_LEN + 14 * sizeof(__u16) + 2 + 1 + 2; + int size = smb_get_xmitsize(server, overhead); + + VERBOSE("xmit=%d, size=%d\n", server->opt.max_xmit, size); + + return size; +} + +/* + * Convert SMB error codes to -E... errno values. + */ +int +smb_errno(struct smb_request *req) +{ + int errcls = req->rq_rcls; + int error = req->rq_err; + char *class = "Unknown"; + + VERBOSE("errcls %d code %d from command 0x%x\n", + errcls, error, SMB_CMD(req->rq_header)); + + if (errcls == ERRDOS) { + switch (error) { + case ERRbadfunc: + return -EINVAL; + case ERRbadfile: + case ERRbadpath: + return -ENOENT; + case ERRnofids: + return -EMFILE; + case ERRnoaccess: + return -EACCES; + case ERRbadfid: + return -EBADF; + case ERRbadmcb: + return -EREMOTEIO; + case ERRnomem: + return -ENOMEM; + case ERRbadmem: + return -EFAULT; + case ERRbadenv: + case ERRbadformat: + return -EREMOTEIO; + case ERRbadaccess: + return -EACCES; + case ERRbaddata: + return -E2BIG; + case ERRbaddrive: + return -ENXIO; + case ERRremcd: + return -EREMOTEIO; + case ERRdiffdevice: + return -EXDEV; + case ERRnofiles: + return -ENOENT; + case ERRbadshare: + return -ETXTBSY; + case ERRlock: + return -EDEADLK; + case ERRfilexists: + return -EEXIST; + case ERROR_INVALID_PARAMETER: + return -EINVAL; + case ERROR_DISK_FULL: + return -ENOSPC; + case ERROR_INVALID_NAME: + return -ENOENT; + case ERROR_DIR_NOT_EMPTY: + return -ENOTEMPTY; + case ERROR_NOT_LOCKED: + return -ENOLCK; + case ERROR_ALREADY_EXISTS: + return -EEXIST; + default: + class = "ERRDOS"; + goto err_unknown; + } + } else if (errcls == ERRSRV) { + switch (error) { + /* N.B. This is wrong ... EIO ? */ + case ERRerror: + return -ENFILE; + case ERRbadpw: + return -EINVAL; + case ERRbadtype: + case ERRtimeout: + return -EIO; + case ERRaccess: + return -EACCES; + /* + * This is a fatal error, as it means the "tree ID" + * for this connection is no longer valid. We map + * to a special error code and get a new connection. + */ + case ERRinvnid: + return -EBADSLT; + default: + class = "ERRSRV"; + goto err_unknown; + } + } else if (errcls == ERRHRD) { + switch (error) { + case ERRnowrite: + return -EROFS; + case ERRbadunit: + return -ENODEV; + case ERRnotready: + return -EUCLEAN; + case ERRbadcmd: + case ERRdata: + return -EIO; + case ERRbadreq: + return -ERANGE; + case ERRbadshare: + return -ETXTBSY; + case ERRlock: + return -EDEADLK; + case ERRdiskfull: + return -ENOSPC; + default: + class = "ERRHRD"; + goto err_unknown; + } + } else if (errcls == ERRCMD) { + class = "ERRCMD"; + } else if (errcls == SUCCESS) { + return 0; /* This is the only valid 0 return */ + } + +err_unknown: + printk(KERN_ERR "smb_errno: class %s, code %d from command 0x%x\n", + class, error, SMB_CMD(req->rq_header)); + return -EIO; +} + +/* smb_request_ok: We expect the server to be locked. Then we do the + request and check the answer completely. When smb_request_ok + returns 0, you can be quite sure that everything went well. When + the answer is <=0, the returned number is a valid unix errno. */ + +static int +smb_request_ok(struct smb_request *req, int command, int wct, int bcc) +{ + int result; + + req->rq_resp_wct = wct; + req->rq_resp_bcc = bcc; + + result = smb_add_request(req); + if (result != 0) { + DEBUG1("smb_request failed\n"); + goto out; + } + + if (smb_valid_packet(req->rq_header) != 0) { + PARANOIA("invalid packet!\n"); + goto out; + } + + result = smb_verify(req->rq_header, command, wct, bcc); + +out: + return result; +} + +/* + * This implements the NEWCONN ioctl. It installs the server pid, + * sets server->state to CONN_VALID, and wakes up the waiting process. + */ +int +smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt) +{ + struct file *filp; + struct sock *sk; + int error; + + VERBOSE("fd=%d, pid=%d\n", opt->fd, current->pid); + + smb_lock_server(server); + + /* + * Make sure we don't already have a valid connection ... + */ + error = -EINVAL; + if (server->state == CONN_VALID) + goto out; + + error = -EACCES; + if (current_uid() != server->mnt->mounted_uid && + !capable(CAP_SYS_ADMIN)) + goto out; + + error = -EBADF; + filp = fget(opt->fd); + if (!filp) + goto out; + if (!smb_valid_socket(filp->f_path.dentry->d_inode)) + goto out_putf; + + server->sock_file = filp; + server->conn_pid = get_pid(task_pid(current)); + server->opt = *opt; + server->generation += 1; + server->state = CONN_VALID; + error = 0; + + if (server->conn_error) { + /* + * conn_error is the returncode we originally decided to + * drop the old connection on. This message should be positive + * and not make people ask questions on why smbfs is printing + * error messages ... + */ + printk(KERN_INFO "SMB connection re-established (%d)\n", + server->conn_error); + server->conn_error = 0; + } + + /* + * Store the server in sock user_data (Only used by sunrpc) + */ + sk = SOCKET_I(filp->f_path.dentry->d_inode)->sk; + sk->sk_user_data = server; + + /* chain into the data_ready callback */ + server->data_ready = xchg(&sk->sk_data_ready, smb_data_ready); + + /* check if we have an old smbmount that uses seconds for the + serverzone */ + if (server->opt.serverzone > 12*60 || server->opt.serverzone < -12*60) + server->opt.serverzone /= 60; + + /* now that we have an established connection we can detect the server + type and enable bug workarounds */ + if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) + install_ops(server->ops, &smb_ops_core); + else if (server->opt.protocol == SMB_PROTOCOL_LANMAN2) + install_ops(server->ops, &smb_ops_os2); + else if (server->opt.protocol == SMB_PROTOCOL_NT1 && + (server->opt.max_xmit < 0x1000) && + !(server->opt.capabilities & SMB_CAP_NT_SMBS)) { + /* FIXME: can we kill the WIN95 flag now? */ + server->mnt->flags |= SMB_MOUNT_WIN95; + VERBOSE("detected WIN95 server\n"); + install_ops(server->ops, &smb_ops_win95); + } else { + /* + * Samba has max_xmit 65535 + * NT4spX has max_xmit 4536 (or something like that) + * win2k has ... + */ + VERBOSE("detected NT1 (Samba, NT4/5) server\n"); + install_ops(server->ops, &smb_ops_winNT); + } + + /* FIXME: the win9x code wants to modify these ... (seek/trunc bug) */ + if (server->mnt->flags & SMB_MOUNT_OLDATTR) { + server->ops->getattr = smb_proc_getattr_core; + } else if (server->mnt->flags & SMB_MOUNT_DIRATTR) { + server->ops->getattr = smb_proc_getattr_ff; + } + + /* Decode server capabilities */ + if (server->opt.capabilities & SMB_CAP_LARGE_FILES) { + /* Should be ok to set this now, as no one can access the + mount until the connection has been established. */ + SB_of(server)->s_maxbytes = ~0ULL >> 1; + VERBOSE("LFS enabled\n"); + } + if (server->opt.capabilities & SMB_CAP_UNICODE) { + server->mnt->flags |= SMB_MOUNT_UNICODE; + VERBOSE("Unicode enabled\n"); + } else { + server->mnt->flags &= ~SMB_MOUNT_UNICODE; + } +#if 0 + /* flags we may test for other patches ... */ + if (server->opt.capabilities & SMB_CAP_LARGE_READX) { + VERBOSE("Large reads enabled\n"); + } + if (server->opt.capabilities & SMB_CAP_LARGE_WRITEX) { + VERBOSE("Large writes enabled\n"); + } +#endif + if (server->opt.capabilities & SMB_CAP_UNIX) { + struct inode *inode; + VERBOSE("Using UNIX CIFS extensions\n"); + install_ops(server->ops, &smb_ops_unix); + inode = SB_of(server)->s_root->d_inode; + if (inode) + inode->i_op = &smb_dir_inode_operations_unix; + } + + VERBOSE("protocol=%d, max_xmit=%d, pid=%d capabilities=0x%x\n", + server->opt.protocol, server->opt.max_xmit, + pid_nr(server->conn_pid), server->opt.capabilities); + + /* FIXME: this really should be done by smbmount. */ + if (server->opt.max_xmit > SMB_MAX_PACKET_SIZE) { + server->opt.max_xmit = SMB_MAX_PACKET_SIZE; + } + + smb_unlock_server(server); + smbiod_wake_up(); + if (server->opt.capabilities & SMB_CAP_UNIX) + smb_proc_query_cifsunix(server); + + server->conn_complete++; + wake_up_interruptible_all(&server->conn_wq); + return error; + +out: + smb_unlock_server(server); + smbiod_wake_up(); + return error; + +out_putf: + fput(filp); + goto out; +} + +/* smb_setup_header: We completely set up the packet. You only have to + insert the command-specific fields */ + +__u8 * +smb_setup_header(struct smb_request *req, __u8 command, __u16 wct, __u16 bcc) +{ + __u32 xmit_len = SMB_HEADER_LEN + wct * sizeof(__u16) + bcc + 2; + __u8 *p = req->rq_header; + struct smb_sb_info *server = req->rq_server; + + p = smb_encode_smb_length(p, xmit_len - 4); + + *p++ = 0xff; + *p++ = 'S'; + *p++ = 'M'; + *p++ = 'B'; + *p++ = command; + + memset(p, '\0', 19); + p += 19; + p += 8; + + if (server->opt.protocol > SMB_PROTOCOL_CORE) { + int flags = SMB_FLAGS_CASELESS_PATHNAMES; + int flags2 = SMB_FLAGS2_LONG_PATH_COMPONENTS | + SMB_FLAGS2_EXTENDED_ATTRIBUTES; /* EA? not really ... */ + + *(req->rq_header + smb_flg) = flags; + if (server->mnt->flags & SMB_MOUNT_UNICODE) + flags2 |= SMB_FLAGS2_UNICODE_STRINGS; + WSET(req->rq_header, smb_flg2, flags2); + } + *p++ = wct; /* wct */ + p += 2 * wct; + WSET(p, 0, bcc); + + /* Include the header in the data to send */ + req->rq_iovlen = 1; + req->rq_iov[0].iov_base = req->rq_header; + req->rq_iov[0].iov_len = xmit_len - bcc; + + return req->rq_buffer; +} + +static void +smb_setup_bcc(struct smb_request *req, __u8 *p) +{ + u16 bcc = p - req->rq_buffer; + u8 *pbcc = req->rq_header + SMB_HEADER_LEN + 2*SMB_WCT(req->rq_header); + + WSET(pbcc, 0, bcc); + + smb_encode_smb_length(req->rq_header, SMB_HEADER_LEN + + 2*SMB_WCT(req->rq_header) - 2 + bcc); + + /* Include the "bytes" in the data to send */ + req->rq_iovlen = 2; + req->rq_iov[1].iov_base = req->rq_buffer; + req->rq_iov[1].iov_len = bcc; +} + +static int +smb_proc_seek(struct smb_sb_info *server, __u16 fileid, + __u16 mode, off_t offset) +{ + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + smb_setup_header(req, SMBlseek, 4, 0); + WSET(req->rq_header, smb_vwv0, fileid); + WSET(req->rq_header, smb_vwv1, mode); + DSET(req->rq_header, smb_vwv2, offset); + req->rq_flags |= SMB_REQ_NORETRY; + + result = smb_request_ok(req, SMBlseek, 2, 0); + if (result < 0) { + result = 0; + goto out_free; + } + + result = DVAL(req->rq_header, smb_vwv0); +out_free: + smb_rput(req); +out: + return result; +} + +static int +smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish) +{ + struct inode *ino = dentry->d_inode; + struct smb_inode_info *ei = SMB_I(ino); + int mode, read_write = 0x42, read_only = 0x40; + int res; + char *p; + struct smb_request *req; + + /* + * Attempt to open r/w, unless there are no write privileges. + */ + mode = read_write; + if (!(ino->i_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) + mode = read_only; +#if 0 + /* FIXME: why is this code not in? below we fix it so that a caller + wanting RO doesn't get RW. smb_revalidate_inode does some + optimization based on access mode. tail -f needs it to be correct. + + We must open rw since we don't do the open if called a second time + with different 'wish'. Is that not supported by smb servers? */ + if (!(wish & (O_WRONLY | O_RDWR))) + mode = read_only; +#endif + + res = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + retry: + p = smb_setup_header(req, SMBopen, 2, 0); + WSET(req->rq_header, smb_vwv0, mode); + WSET(req->rq_header, smb_vwv1, aSYSTEM | aHIDDEN | aDIR); + res = smb_simple_encode_path(req, &p, dentry, NULL); + if (res < 0) + goto out_free; + smb_setup_bcc(req, p); + + res = smb_request_ok(req, SMBopen, 7, 0); + if (res != 0) { + if (mode == read_write && + (res == -EACCES || res == -ETXTBSY || res == -EROFS)) + { + VERBOSE("%s/%s R/W failed, error=%d, retrying R/O\n", + DENTRY_PATH(dentry), res); + mode = read_only; + req->rq_flags = 0; + goto retry; + } + goto out_free; + } + /* We should now have data in vwv[0..6]. */ + + ei->fileid = WVAL(req->rq_header, smb_vwv0); + ei->attr = WVAL(req->rq_header, smb_vwv1); + /* smb_vwv2 has mtime */ + /* smb_vwv4 has size */ + ei->access = (WVAL(req->rq_header, smb_vwv6) & SMB_ACCMASK); + ei->open = server->generation; + +out_free: + smb_rput(req); +out: + return res; +} + +/* + * Make sure the file is open, and check that the access + * is compatible with the desired access. + */ +int +smb_open(struct dentry *dentry, int wish) +{ + struct inode *inode = dentry->d_inode; + int result; + __u16 access; + + result = -ENOENT; + if (!inode) { + printk(KERN_ERR "smb_open: no inode for dentry %s/%s\n", + DENTRY_PATH(dentry)); + goto out; + } + + if (!smb_is_open(inode)) { + struct smb_sb_info *server = server_from_inode(inode); + result = 0; + if (!smb_is_open(inode)) + result = smb_proc_open(server, dentry, wish); + if (result) + goto out; + /* + * A successful open means the path is still valid ... + */ + smb_renew_times(dentry); + } + + /* + * Check whether the access is compatible with the desired mode. + */ + result = 0; + access = SMB_I(inode)->access; + if (access != wish && access != SMB_O_RDWR) { + PARANOIA("%s/%s access denied, access=%x, wish=%x\n", + DENTRY_PATH(dentry), access, wish); + result = -EACCES; + } +out: + return result; +} + +static int +smb_proc_close(struct smb_sb_info *server, __u16 fileid, __u32 mtime) +{ + struct smb_request *req; + int result = -ENOMEM; + + if (! (req = smb_alloc_request(server, 0))) + goto out; + + smb_setup_header(req, SMBclose, 3, 0); + WSET(req->rq_header, smb_vwv0, fileid); + DSET(req->rq_header, smb_vwv1, utc2local(server, mtime)); + req->rq_flags |= SMB_REQ_NORETRY; + result = smb_request_ok(req, SMBclose, 0, 0); + + smb_rput(req); +out: + return result; +} + +/* + * Win NT 4.0 has an apparent bug in that it fails to update the + * modify time when writing to a file. As a workaround, we update + * both modify and access time locally, and post the times to the + * server when closing the file. + */ +static int +smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino) +{ + struct smb_inode_info *ei = SMB_I(ino); + int result = 0; + if (smb_is_open(ino)) + { + /* + * We clear the open flag in advance, in case another + * process observes the value while we block below. + */ + ei->open = 0; + + /* + * Kludge alert: SMB timestamps are accurate only to + * two seconds ... round the times to avoid needless + * cache invalidations! + */ + if (ino->i_mtime.tv_sec & 1) { + ino->i_mtime.tv_sec--; + ino->i_mtime.tv_nsec = 0; + } + if (ino->i_atime.tv_sec & 1) { + ino->i_atime.tv_sec--; + ino->i_atime.tv_nsec = 0; + } + /* + * If the file is open with write permissions, + * update the time stamps to sync mtime and atime. + */ + if ((server->opt.capabilities & SMB_CAP_UNIX) == 0 && + (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) && + !(ei->access == SMB_O_RDONLY)) + { + struct smb_fattr fattr; + smb_get_inode_attr(ino, &fattr); + smb_proc_setattr_ext(server, ino, &fattr); + } + + result = smb_proc_close(server, ei->fileid, ino->i_mtime.tv_sec); + /* + * Force a revalidation after closing ... some servers + * don't post the size until the file has been closed. + */ + if (server->opt.protocol < SMB_PROTOCOL_NT1) + ei->oldmtime = 0; + ei->closed = jiffies; + } + return result; +} + +int +smb_close(struct inode *ino) +{ + int result = 0; + + if (smb_is_open(ino)) { + struct smb_sb_info *server = server_from_inode(ino); + result = smb_proc_close_inode(server, ino); + } + return result; +} + +/* + * This is used to close a file following a failed instantiate. + * Since we don't have an inode, we can't use any of the above. + */ +int +smb_close_fileid(struct dentry *dentry, __u16 fileid) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + int result; + + result = smb_proc_close(server, fileid, get_seconds()); + return result; +} + +/* In smb_proc_read and smb_proc_write we do not retry, because the + file-id would not be valid after a reconnection. */ + +static void +smb_proc_read_data(struct smb_request *req) +{ + req->rq_iov[0].iov_base = req->rq_buffer; + req->rq_iov[0].iov_len = 3; + + req->rq_iov[1].iov_base = req->rq_page; + req->rq_iov[1].iov_len = req->rq_rsize; + req->rq_iovlen = 2; + + req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd; +} + +static int +smb_proc_read(struct inode *inode, loff_t offset, int count, char *data) +{ + struct smb_sb_info *server = server_from_inode(inode); + __u16 returned_count, data_len; + unsigned char *buf; + int result; + struct smb_request *req; + u8 rbuf[4]; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + smb_setup_header(req, SMBread, 5, 0); + buf = req->rq_header; + WSET(buf, smb_vwv0, SMB_I(inode)->fileid); + WSET(buf, smb_vwv1, count); + DSET(buf, smb_vwv2, offset); + WSET(buf, smb_vwv4, 0); + + req->rq_page = data; + req->rq_rsize = count; + req->rq_callback = smb_proc_read_data; + req->rq_buffer = rbuf; + req->rq_flags |= SMB_REQ_NORETRY | SMB_REQ_STATIC; + + result = smb_request_ok(req, SMBread, 5, -1); + if (result < 0) + goto out_free; + returned_count = WVAL(req->rq_header, smb_vwv0); + + data_len = WVAL(rbuf, 1); + + if (returned_count != data_len) { + printk(KERN_NOTICE "smb_proc_read: returned != data_len\n"); + printk(KERN_NOTICE "smb_proc_read: ret_c=%d, data_len=%d\n", + returned_count, data_len); + } + result = data_len; + +out_free: + smb_rput(req); +out: + VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n", + inode->i_ino, SMB_I(inode)->fileid, count, result); + return result; +} + +static int +smb_proc_write(struct inode *inode, loff_t offset, int count, const char *data) +{ + struct smb_sb_info *server = server_from_inode(inode); + int result; + u16 fileid = SMB_I(inode)->fileid; + u8 buf[4]; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld\n", + inode->i_ino, fileid, count, offset); + + smb_setup_header(req, SMBwrite, 5, count + 3); + WSET(req->rq_header, smb_vwv0, fileid); + WSET(req->rq_header, smb_vwv1, count); + DSET(req->rq_header, smb_vwv2, offset); + WSET(req->rq_header, smb_vwv4, 0); + + buf[0] = 1; + WSET(buf, 1, count); /* yes, again ... */ + req->rq_iov[1].iov_base = buf; + req->rq_iov[1].iov_len = 3; + req->rq_iov[2].iov_base = (char *) data; + req->rq_iov[2].iov_len = count; + req->rq_iovlen = 3; + req->rq_flags |= SMB_REQ_NORETRY; + + result = smb_request_ok(req, SMBwrite, 1, 0); + if (result >= 0) + result = WVAL(req->rq_header, smb_vwv0); + + smb_rput(req); +out: + return result; +} + +/* + * In smb_proc_readX and smb_proc_writeX we do not retry, because the + * file-id would not be valid after a reconnection. + */ + +#define SMB_READX_MAX_PAD 64 +static void +smb_proc_readX_data(struct smb_request *req) +{ + /* header length, excluding the netbios length (-4) */ + int hdrlen = SMB_HEADER_LEN + req->rq_resp_wct*2 - 2; + int data_off = WVAL(req->rq_header, smb_vwv6); + + /* + * Some genius made the padding to the data bytes arbitrary. + * So we must first calculate the amount of padding used by the server. + */ + data_off -= hdrlen; + if (data_off > SMB_READX_MAX_PAD || data_off < 0) { + PARANOIA("offset is larger than SMB_READX_MAX_PAD or negative!\n"); + PARANOIA("%d > %d || %d < 0\n", data_off, SMB_READX_MAX_PAD, data_off); + req->rq_rlen = req->rq_bufsize + 1; + return; + } + req->rq_iov[0].iov_base = req->rq_buffer; + req->rq_iov[0].iov_len = data_off; + + req->rq_iov[1].iov_base = req->rq_page; + req->rq_iov[1].iov_len = req->rq_rsize; + req->rq_iovlen = 2; + + req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd; +} + +static int +smb_proc_readX(struct inode *inode, loff_t offset, int count, char *data) +{ + struct smb_sb_info *server = server_from_inode(inode); + unsigned char *buf; + int result; + struct smb_request *req; + static char pad[SMB_READX_MAX_PAD]; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + smb_setup_header(req, SMBreadX, 12, 0); + buf = req->rq_header; + WSET(buf, smb_vwv0, 0x00ff); + WSET(buf, smb_vwv1, 0); + WSET(buf, smb_vwv2, SMB_I(inode)->fileid); + DSET(buf, smb_vwv3, (u32)offset); /* low 32 bits */ + WSET(buf, smb_vwv5, count); + WSET(buf, smb_vwv6, 0); + DSET(buf, smb_vwv7, 0); + WSET(buf, smb_vwv9, 0); + DSET(buf, smb_vwv10, (u32)(offset >> 32)); /* high 32 bits */ + WSET(buf, smb_vwv11, 0); + + req->rq_page = data; + req->rq_rsize = count; + req->rq_callback = smb_proc_readX_data; + req->rq_buffer = pad; + req->rq_bufsize = SMB_READX_MAX_PAD; + req->rq_flags |= SMB_REQ_STATIC | SMB_REQ_NORETRY; + + result = smb_request_ok(req, SMBreadX, 12, -1); + if (result < 0) + goto out_free; + result = WVAL(req->rq_header, smb_vwv5); + +out_free: + smb_rput(req); +out: + VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n", + inode->i_ino, SMB_I(inode)->fileid, count, result); + return result; +} + +static int +smb_proc_writeX(struct inode *inode, loff_t offset, int count, const char *data) +{ + struct smb_sb_info *server = server_from_inode(inode); + int result; + u8 *p; + static u8 pad[4]; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld\n", + inode->i_ino, SMB_I(inode)->fileid, count, offset); + + p = smb_setup_header(req, SMBwriteX, 14, count + 1); + WSET(req->rq_header, smb_vwv0, 0x00ff); + WSET(req->rq_header, smb_vwv1, 0); + WSET(req->rq_header, smb_vwv2, SMB_I(inode)->fileid); + DSET(req->rq_header, smb_vwv3, (u32)offset); /* low 32 bits */ + DSET(req->rq_header, smb_vwv5, 0); + WSET(req->rq_header, smb_vwv7, 0); /* write mode */ + WSET(req->rq_header, smb_vwv8, 0); + WSET(req->rq_header, smb_vwv9, 0); + WSET(req->rq_header, smb_vwv10, count); /* data length */ + WSET(req->rq_header, smb_vwv11, smb_vwv12 + 2 + 1); + DSET(req->rq_header, smb_vwv12, (u32)(offset >> 32)); + + req->rq_iov[1].iov_base = pad; + req->rq_iov[1].iov_len = 1; + req->rq_iov[2].iov_base = (char *) data; + req->rq_iov[2].iov_len = count; + req->rq_iovlen = 3; + req->rq_flags |= SMB_REQ_NORETRY; + + result = smb_request_ok(req, SMBwriteX, 6, 0); + if (result >= 0) + result = WVAL(req->rq_header, smb_vwv2); + + smb_rput(req); +out: + return result; +} + +int +smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + char *p; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + p = smb_setup_header(req, SMBcreate, 3, 0); + WSET(req->rq_header, smb_vwv0, attr); + DSET(req->rq_header, smb_vwv1, utc2local(server, ctime)); + result = smb_simple_encode_path(req, &p, dentry, NULL); + if (result < 0) + goto out_free; + smb_setup_bcc(req, p); + + result = smb_request_ok(req, SMBcreate, 1, 0); + if (result < 0) + goto out_free; + + *fileid = WVAL(req->rq_header, smb_vwv0); + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +int +smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry) +{ + struct smb_sb_info *server = server_from_dentry(old_dentry); + char *p; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + p = smb_setup_header(req, SMBmv, 1, 0); + WSET(req->rq_header, smb_vwv0, aSYSTEM | aHIDDEN | aDIR); + result = smb_simple_encode_path(req, &p, old_dentry, NULL); + if (result < 0) + goto out_free; + result = smb_simple_encode_path(req, &p, new_dentry, NULL); + if (result < 0) + goto out_free; + smb_setup_bcc(req, p); + + if ((result = smb_request_ok(req, SMBmv, 0, 0)) < 0) + goto out_free; + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +/* + * Code common to mkdir and rmdir. + */ +static int +smb_proc_generic_command(struct dentry *dentry, __u8 command) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + char *p; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + p = smb_setup_header(req, command, 0, 0); + result = smb_simple_encode_path(req, &p, dentry, NULL); + if (result < 0) + goto out_free; + smb_setup_bcc(req, p); + + result = smb_request_ok(req, command, 0, 0); + if (result < 0) + goto out_free; + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +int +smb_proc_mkdir(struct dentry *dentry) +{ + return smb_proc_generic_command(dentry, SMBmkdir); +} + +int +smb_proc_rmdir(struct dentry *dentry) +{ + return smb_proc_generic_command(dentry, SMBrmdir); +} + +#if SMBFS_POSIX_UNLINK +/* + * Removes readonly attribute from a file. Used by unlink to give posix + * semantics. + */ +static int +smb_set_rw(struct dentry *dentry,struct smb_sb_info *server) +{ + int result; + struct smb_fattr fattr; + + /* FIXME: cifsUE should allow removing a readonly file. */ + + /* first get current attribute */ + smb_init_dirent(server, &fattr); + result = server->ops->getattr(server, dentry, &fattr); + smb_finish_dirent(server, &fattr); + if (result < 0) + return result; + + /* if RONLY attribute is set, remove it */ + if (fattr.attr & aRONLY) { /* read only attribute is set */ + fattr.attr &= ~aRONLY; + result = smb_proc_setattr_core(server, dentry, fattr.attr); + } + return result; +} +#endif + +int +smb_proc_unlink(struct dentry *dentry) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + int flag = 0; + char *p; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + retry: + p = smb_setup_header(req, SMBunlink, 1, 0); + WSET(req->rq_header, smb_vwv0, aSYSTEM | aHIDDEN); + result = smb_simple_encode_path(req, &p, dentry, NULL); + if (result < 0) + goto out_free; + smb_setup_bcc(req, p); + + if ((result = smb_request_ok(req, SMBunlink, 0, 0)) < 0) { +#if SMBFS_POSIX_UNLINK + if (result == -EACCES && !flag) { + /* Posix semantics is for the read-only state + of a file to be ignored in unlink(). In the + SMB world a unlink() is refused on a + read-only file. To make things easier for + unix users we try to override the files + permission if the unlink fails with the + right error. + This introduces a race condition that could + lead to a file being written by someone who + shouldn't have access, but as far as I can + tell that is unavoidable */ + + /* remove RONLY attribute and try again */ + result = smb_set_rw(dentry,server); + if (result == 0) { + flag = 1; + req->rq_flags = 0; + goto retry; + } + } +#endif + goto out_free; + } + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +int +smb_proc_flush(struct smb_sb_info *server, __u16 fileid) +{ + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + smb_setup_header(req, SMBflush, 1, 0); + WSET(req->rq_header, smb_vwv0, fileid); + req->rq_flags |= SMB_REQ_NORETRY; + result = smb_request_ok(req, SMBflush, 0, 0); + + smb_rput(req); +out: + return result; +} + +static int +smb_proc_trunc32(struct inode *inode, loff_t length) +{ + /* + * Writing 0bytes is old-SMB magic for truncating files. + * MAX_NON_LFS should prevent this from being called with a too + * large offset. + */ + return smb_proc_write(inode, length, 0, NULL); +} + +static int +smb_proc_trunc64(struct inode *inode, loff_t length) +{ + struct smb_sb_info *server = server_from_inode(inode); + int result; + char *param; + char *data; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 14))) + goto out; + + param = req->rq_buffer; + data = req->rq_buffer + 6; + + /* FIXME: must we also set allocation size? winNT seems to do that */ + WSET(param, 0, SMB_I(inode)->fileid); + WSET(param, 2, SMB_SET_FILE_END_OF_FILE_INFO); + WSET(param, 4, 0); + LSET(data, 0, length); + + req->rq_trans2_command = TRANSACT2_SETFILEINFO; + req->rq_ldata = 8; + req->rq_data = data; + req->rq_lparm = 6; + req->rq_parm = param; + req->rq_flags |= SMB_REQ_NORETRY; + result = smb_add_request(req); + if (result < 0) + goto out_free; + + result = 0; + if (req->rq_rcls != 0) + result = smb_errno(req); + +out_free: + smb_rput(req); +out: + return result; +} + +static int +smb_proc_trunc95(struct inode *inode, loff_t length) +{ + struct smb_sb_info *server = server_from_inode(inode); + int result = smb_proc_trunc32(inode, length); + + /* + * win9x doesn't appear to update the size immediately. + * It will return the old file size after the truncate, + * confusing smbfs. So we force an update. + * + * FIXME: is this still necessary? + */ + smb_proc_flush(server, SMB_I(inode)->fileid); + return result; +} + +static void +smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr) +{ + memset(fattr, 0, sizeof(*fattr)); + + fattr->f_nlink = 1; + fattr->f_uid = server->mnt->uid; + fattr->f_gid = server->mnt->gid; + fattr->f_unix = 0; +} + +static void +smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr) +{ + if (fattr->f_unix) + return; + + fattr->f_mode = server->mnt->file_mode; + if (fattr->attr & aDIR) { + fattr->f_mode = server->mnt->dir_mode; + fattr->f_size = SMB_ST_BLKSIZE; + } + /* Check the read-only flag */ + if (fattr->attr & aRONLY) + fattr->f_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + + /* How many 512 byte blocks do we need for this file? */ + fattr->f_blocks = 0; + if (fattr->f_size != 0) + fattr->f_blocks = 1 + ((fattr->f_size-1) >> 9); + return; +} + +void +smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr, + struct super_block *sb) +{ + smb_init_dirent(server, fattr); + fattr->attr = aDIR; + fattr->f_ino = 2; /* traditional root inode number */ + fattr->f_mtime = current_fs_time(sb); + smb_finish_dirent(server, fattr); +} + +/* + * Decode a dirent for old protocols + * + * qname is filled with the decoded, and possibly translated, name. + * fattr receives decoded attributes + * + * Bugs Noted: + * (1) Pathworks servers may pad the name with extra spaces. + */ +static char * +smb_decode_short_dirent(struct smb_sb_info *server, char *p, + struct qstr *qname, struct smb_fattr *fattr, + unsigned char *name_buf) +{ + int len; + + /* + * SMB doesn't have a concept of inode numbers ... + */ + smb_init_dirent(server, fattr); + fattr->f_ino = 0; /* FIXME: do we need this? */ + + p += SMB_STATUS_SIZE; /* reserved (search_status) */ + fattr->attr = *p; + fattr->f_mtime.tv_sec = date_dos2unix(server, WVAL(p, 3), WVAL(p, 1)); + fattr->f_mtime.tv_nsec = 0; + fattr->f_size = DVAL(p, 5); + fattr->f_ctime = fattr->f_mtime; + fattr->f_atime = fattr->f_mtime; + qname->name = p + 9; + len = strnlen(qname->name, 12); + + /* + * Trim trailing blanks for Pathworks servers + */ + while (len > 2 && qname->name[len-1] == ' ') + len--; + + smb_finish_dirent(server, fattr); + +#if 0 + /* FIXME: These only work for ascii chars, and recent smbmount doesn't + allow the flag to be set anyway. It kills const. Remove? */ + switch (server->opt.case_handling) { + case SMB_CASE_UPPER: + str_upper(entry->name, len); + break; + case SMB_CASE_LOWER: + str_lower(entry->name, len); + break; + default: + break; + } +#endif + + qname->len = 0; + len = server->ops->convert(name_buf, SMB_MAXNAMELEN, + qname->name, len, + server->remote_nls, server->local_nls); + if (len > 0) { + qname->len = len; + qname->name = name_buf; + DEBUG1("len=%d, name=%.*s\n",qname->len,qname->len,qname->name); + } + + return p + 22; +} + +/* + * This routine is used to read in directory entries from the network. + * Note that it is for short directory name seeks, i.e.: protocol < + * SMB_PROTOCOL_LANMAN2 + */ +static int +smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir, + struct smb_cache_control *ctl) +{ + struct dentry *dir = filp->f_path.dentry; + struct smb_sb_info *server = server_from_dentry(dir); + struct qstr qname; + struct smb_fattr fattr; + char *p; + int result; + int i, first, entries_seen, entries; + int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE; + __u16 bcc; + __u16 count; + char status[SMB_STATUS_SIZE]; + static struct qstr mask = { + .name = "*.*", + .len = 3, + }; + unsigned char *last_status; + struct smb_request *req; + unsigned char *name_buf; + + VERBOSE("%s/%s\n", DENTRY_PATH(dir)); + + lock_kernel(); + + result = -ENOMEM; + if (! (name_buf = kmalloc(SMB_MAXNAMELEN, GFP_KERNEL))) + goto out; + + first = 1; + entries = 0; + entries_seen = 2; /* implicit . and .. */ + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, server->opt.max_xmit))) + goto out_name; + + while (1) { + p = smb_setup_header(req, SMBsearch, 2, 0); + WSET(req->rq_header, smb_vwv0, entries_asked); + WSET(req->rq_header, smb_vwv1, aDIR); + if (first == 1) { + result = smb_simple_encode_path(req, &p, dir, &mask); + if (result < 0) + goto out_free; + if (p + 3 > (char *)req->rq_buffer + req->rq_bufsize) { + result = -ENAMETOOLONG; + goto out_free; + } + *p++ = 5; + WSET(p, 0, 0); + p += 2; + first = 0; + } else { + if (p + 5 + SMB_STATUS_SIZE > + (char *)req->rq_buffer + req->rq_bufsize) { + result = -ENAMETOOLONG; + goto out_free; + } + + *p++ = 4; + *p++ = 0; + *p++ = 5; + WSET(p, 0, SMB_STATUS_SIZE); + p += 2; + memcpy(p, status, SMB_STATUS_SIZE); + p += SMB_STATUS_SIZE; + } + + smb_setup_bcc(req, p); + + result = smb_request_ok(req, SMBsearch, 1, -1); + if (result < 0) { + if ((req->rq_rcls == ERRDOS) && + (req->rq_err == ERRnofiles)) + break; + goto out_free; + } + count = WVAL(req->rq_header, smb_vwv0); + if (count <= 0) + break; + + result = -EIO; + bcc = smb_bcc(req->rq_header); + if (bcc != count * SMB_DIRINFO_SIZE + 3) + goto out_free; + p = req->rq_buffer + 3; + + + /* Make sure the response fits in the buffer. Fixed sized + entries means we don't have to check in the decode loop. */ + + last_status = req->rq_buffer + 3 + (count-1) * SMB_DIRINFO_SIZE; + + if (last_status + SMB_DIRINFO_SIZE >= + req->rq_buffer + req->rq_bufsize) { + printk(KERN_ERR "smb_proc_readdir_short: " + "last dir entry outside buffer! " + "%d@%p %d@%p\n", SMB_DIRINFO_SIZE, last_status, + req->rq_bufsize, req->rq_buffer); + goto out_free; + } + + /* Read the last entry into the status field. */ + memcpy(status, last_status, SMB_STATUS_SIZE); + + + /* Now we are ready to parse smb directory entries. */ + + for (i = 0; i < count; i++) { + p = smb_decode_short_dirent(server, p, + &qname, &fattr, name_buf); + if (qname.len == 0) + continue; + + if (entries_seen == 2 && qname.name[0] == '.') { + if (qname.len == 1) + continue; + if (qname.name[1] == '.' && qname.len == 2) + continue; + } + if (!smb_fill_cache(filp, dirent, filldir, ctl, + &qname, &fattr)) + ; /* stop reading? */ + entries_seen++; + } + } + result = entries; + +out_free: + smb_rput(req); +out_name: + kfree(name_buf); +out: + unlock_kernel(); + return result; +} + +static void smb_decode_unix_basic(struct smb_fattr *fattr, struct smb_sb_info *server, char *p) +{ + u64 size, disk_bytes; + + /* FIXME: verify nls support. all is sent as utf8? */ + + fattr->f_unix = 1; + fattr->f_mode = 0; + + /* FIXME: use the uniqueID from the remote instead? */ + /* 0 L file size in bytes */ + /* 8 L file size on disk in bytes (block count) */ + /* 40 L uid */ + /* 48 L gid */ + /* 56 W file type */ + /* 60 L devmajor */ + /* 68 L devminor */ + /* 76 L unique ID (inode) */ + /* 84 L permissions */ + /* 92 L link count */ + + size = LVAL(p, 0); + disk_bytes = LVAL(p, 8); + + /* + * Some samba versions round up on-disk byte usage + * to 1MB boundaries, making it useless. When seeing + * that, use the size instead. + */ + if (!(disk_bytes & 0xfffff)) + disk_bytes = size+511; + + fattr->f_size = size; + fattr->f_blocks = disk_bytes >> 9; + fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 16)); + fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 24)); + fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 32)); + + if (server->mnt->flags & SMB_MOUNT_UID) + fattr->f_uid = server->mnt->uid; + else + fattr->f_uid = LVAL(p, 40); + + if (server->mnt->flags & SMB_MOUNT_GID) + fattr->f_gid = server->mnt->gid; + else + fattr->f_gid = LVAL(p, 48); + + fattr->f_mode |= smb_filetype_to_mode(WVAL(p, 56)); + + if (S_ISBLK(fattr->f_mode) || S_ISCHR(fattr->f_mode)) { + __u64 major = LVAL(p, 60); + __u64 minor = LVAL(p, 68); + + fattr->f_rdev = MKDEV(major & 0xffffffff, minor & 0xffffffff); + if (MAJOR(fattr->f_rdev) != (major & 0xffffffff) || + MINOR(fattr->f_rdev) != (minor & 0xffffffff)) + fattr->f_rdev = 0; + } + + fattr->f_mode |= LVAL(p, 84); + + if ( (server->mnt->flags & SMB_MOUNT_DMODE) && + (S_ISDIR(fattr->f_mode)) ) + fattr->f_mode = (server->mnt->dir_mode & S_IRWXUGO) | S_IFDIR; + else if ( (server->mnt->flags & SMB_MOUNT_FMODE) && + !(S_ISDIR(fattr->f_mode)) ) + fattr->f_mode = (server->mnt->file_mode & S_IRWXUGO) | + (fattr->f_mode & S_IFMT); + +} + +/* + * Interpret a long filename structure using the specified info level: + * level 1 for anything below NT1 protocol + * level 260 for NT1 protocol + * + * qname is filled with the decoded, and possibly translated, name + * fattr receives decoded attributes. + * + * Bugs Noted: + * (1) Win NT 4.0 appends a null byte to names and counts it in the length! + */ +static char * +smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level, + struct qstr *qname, struct smb_fattr *fattr, + unsigned char *name_buf) +{ + char *result; + unsigned int len = 0; + int n; + __u16 date, time; + int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE); + + /* + * SMB doesn't have a concept of inode numbers ... + */ + smb_init_dirent(server, fattr); + fattr->f_ino = 0; /* FIXME: do we need this? */ + + switch (level) { + case 1: + len = *((unsigned char *) p + 22); + qname->name = p + 23; + result = p + 24 + len; + + date = WVAL(p, 0); + time = WVAL(p, 2); + fattr->f_ctime.tv_sec = date_dos2unix(server, date, time); + fattr->f_ctime.tv_nsec = 0; + + date = WVAL(p, 4); + time = WVAL(p, 6); + fattr->f_atime.tv_sec = date_dos2unix(server, date, time); + fattr->f_atime.tv_nsec = 0; + + date = WVAL(p, 8); + time = WVAL(p, 10); + fattr->f_mtime.tv_sec = date_dos2unix(server, date, time); + fattr->f_mtime.tv_nsec = 0; + fattr->f_size = DVAL(p, 12); + /* ULONG allocation size */ + fattr->attr = WVAL(p, 20); + + VERBOSE("info 1 at %p, len=%d, name=%.*s\n", + p, len, len, qname->name); + break; + case 260: + result = p + WVAL(p, 0); + len = DVAL(p, 60); + if (len > 255) len = 255; + /* NT4 null terminates, unless we are using unicode ... */ + qname->name = p + 94; + if (!unicode && len && qname->name[len-1] == '\0') + len--; + + fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 8)); + fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 16)); + fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 24)); + /* change time (32) */ + fattr->f_size = LVAL(p, 40); + /* alloc size (48) */ + fattr->attr = DVAL(p, 56); + + VERBOSE("info 260 at %p, len=%d, name=%.*s\n", + p, len, len, qname->name); + break; + case SMB_FIND_FILE_UNIX: + result = p + WVAL(p, 0); + qname->name = p + 108; + + len = strlen(qname->name); + /* FIXME: should we check the length?? */ + + p += 8; + smb_decode_unix_basic(fattr, server, p); + VERBOSE("info SMB_FIND_FILE_UNIX at %p, len=%d, name=%.*s\n", + p, len, len, qname->name); + break; + default: + PARANOIA("Unknown info level %d\n", level); + result = p + WVAL(p, 0); + goto out; + } + + smb_finish_dirent(server, fattr); + +#if 0 + /* FIXME: These only work for ascii chars, and recent smbmount doesn't + allow the flag to be set anyway. Remove? */ + switch (server->opt.case_handling) { + case SMB_CASE_UPPER: + str_upper(qname->name, len); + break; + case SMB_CASE_LOWER: + str_lower(qname->name, len); + break; + default: + break; + } +#endif + + qname->len = 0; + n = server->ops->convert(name_buf, SMB_MAXNAMELEN, + qname->name, len, + server->remote_nls, server->local_nls); + if (n > 0) { + qname->len = n; + qname->name = name_buf; + } + +out: + return result; +} + +/* findfirst/findnext flags */ +#define SMB_CLOSE_AFTER_FIRST (1<<0) +#define SMB_CLOSE_IF_END (1<<1) +#define SMB_REQUIRE_RESUME_KEY (1<<2) +#define SMB_CONTINUE_BIT (1<<3) + +/* + * Note: samba-2.0.7 (at least) has a very similar routine, cli_list, in + * source/libsmb/clilist.c. When looking for smb bugs in the readdir code, + * go there for advise. + * + * Bugs Noted: + * (1) When using Info Level 1 Win NT 4.0 truncates directory listings + * for certain patterns of names and/or lengths. The breakage pattern + * is completely reproducible and can be toggled by the creation of a + * single file. (E.g. echo hi >foo breaks, rm -f foo works.) + */ +static int +smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir, + struct smb_cache_control *ctl) +{ + struct dentry *dir = filp->f_path.dentry; + struct smb_sb_info *server = server_from_dentry(dir); + struct qstr qname; + struct smb_fattr fattr; + + unsigned char *p, *lastname; + char *mask, *param; + __u16 command; + int first, entries_seen; + + /* Both NT and OS/2 accept info level 1 (but see note below). */ + int info_level = 260; + const int max_matches = 512; + + unsigned int ff_searchcount = 0; + unsigned int ff_eos = 0; + unsigned int ff_lastname = 0; + unsigned int ff_dir_handle = 0; + unsigned int loop_count = 0; + unsigned int mask_len, i; + int result; + struct smb_request *req; + unsigned char *name_buf; + static struct qstr star = { + .name = "*", + .len = 1, + }; + + lock_kernel(); + + /* + * We always prefer unix style. Use info level 1 for older + * servers that don't do 260. + */ + if (server->opt.capabilities & SMB_CAP_UNIX) + info_level = SMB_FIND_FILE_UNIX; + else if (server->opt.protocol < SMB_PROTOCOL_NT1) + info_level = 1; + + result = -ENOMEM; + if (! (name_buf = kmalloc(SMB_MAXNAMELEN+2, GFP_KERNEL))) + goto out; + if (! (req = smb_alloc_request(server, server->opt.max_xmit))) + goto out_name; + param = req->rq_buffer; + + /* + * Encode the initial path + */ + mask = param + 12; + + result = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dir, &star); + if (result <= 0) + goto out_free; + mask_len = result - 1; /* mask_len is strlen, not #bytes */ + result = 0; + first = 1; + VERBOSE("starting mask_len=%d, mask=%s\n", mask_len, mask); + + entries_seen = 2; + ff_eos = 0; + + while (ff_eos == 0) { + loop_count += 1; + if (loop_count > 10) { + printk(KERN_WARNING "smb_proc_readdir_long: " + "Looping in FIND_NEXT??\n"); + result = -EIO; + break; + } + + if (first != 0) { + command = TRANSACT2_FINDFIRST; + WSET(param, 0, aSYSTEM | aHIDDEN | aDIR); + WSET(param, 2, max_matches); /* max count */ + WSET(param, 4, SMB_CLOSE_IF_END); + WSET(param, 6, info_level); + DSET(param, 8, 0); + } else { + command = TRANSACT2_FINDNEXT; + + VERBOSE("handle=0x%X, lastname=%d, mask=%.*s\n", + ff_dir_handle, ff_lastname, mask_len, mask); + + WSET(param, 0, ff_dir_handle); /* search handle */ + WSET(param, 2, max_matches); /* max count */ + WSET(param, 4, info_level); + DSET(param, 6, 0); + WSET(param, 10, SMB_CONTINUE_BIT|SMB_CLOSE_IF_END); + } + + req->rq_trans2_command = command; + req->rq_ldata = 0; + req->rq_data = NULL; + req->rq_lparm = 12 + mask_len + 1; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) { + PARANOIA("error=%d, breaking\n", result); + break; + } + + if (req->rq_rcls == ERRSRV && req->rq_err == ERRerror) { + /* a damn Win95 bug - sometimes it clags if you + ask it too fast */ + schedule_timeout_interruptible(msecs_to_jiffies(200)); + continue; + } + + if (req->rq_rcls != 0) { + result = smb_errno(req); + PARANOIA("name=%s, result=%d, rcls=%d, err=%d\n", + mask, result, req->rq_rcls, req->rq_err); + break; + } + + /* parse out some important return info */ + if (first != 0) { + ff_dir_handle = WVAL(req->rq_parm, 0); + ff_searchcount = WVAL(req->rq_parm, 2); + ff_eos = WVAL(req->rq_parm, 4); + ff_lastname = WVAL(req->rq_parm, 8); + } else { + ff_searchcount = WVAL(req->rq_parm, 0); + ff_eos = WVAL(req->rq_parm, 2); + ff_lastname = WVAL(req->rq_parm, 6); + } + + if (ff_searchcount == 0) + break; + + /* Now we are ready to parse smb directory entries. */ + + /* point to the data bytes */ + p = req->rq_data; + for (i = 0; i < ff_searchcount; i++) { + /* make sure we stay within the buffer */ + if (p >= req->rq_data + req->rq_ldata) { + printk(KERN_ERR "smb_proc_readdir_long: " + "dirent pointer outside buffer! " + "%p %d@%p\n", + p, req->rq_ldata, req->rq_data); + result = -EIO; /* always a comm. error? */ + goto out_free; + } + + p = smb_decode_long_dirent(server, p, info_level, + &qname, &fattr, name_buf); + + /* ignore . and .. from the server */ + if (entries_seen == 2 && qname.name[0] == '.') { + if (qname.len == 1) + continue; + if (qname.name[1] == '.' && qname.len == 2) + continue; + } + + if (!smb_fill_cache(filp, dirent, filldir, ctl, + &qname, &fattr)) + ; /* stop reading? */ + entries_seen++; + } + + VERBOSE("received %d entries, eos=%d\n", ff_searchcount,ff_eos); + + /* + * We might need the lastname for continuations. + * + * Note that some servers (win95?) point to the filename and + * others (NT4, Samba using NT1) to the dir entry. We assume + * here that those who do not point to a filename do not need + * this info to continue the listing. + * + * OS/2 needs this and talks infolevel 1. + * NetApps want lastname with infolevel 260. + * win2k want lastname with infolevel 260, and points to + * the record not to the name. + * Samba+CifsUnixExt doesn't need lastname. + * + * Both are happy if we return the data they point to. So we do. + * (FIXME: above is not true with win2k) + */ + mask_len = 0; + if (info_level != SMB_FIND_FILE_UNIX && + ff_lastname > 0 && ff_lastname < req->rq_ldata) { + lastname = req->rq_data + ff_lastname; + + switch (info_level) { + case 260: + mask_len = req->rq_ldata - ff_lastname; + break; + case 1: + /* lastname points to a length byte */ + mask_len = *lastname++; + if (ff_lastname + 1 + mask_len > req->rq_ldata) + mask_len = req->rq_ldata - ff_lastname - 1; + break; + } + + /* + * Update the mask string for the next message. + */ + if (mask_len > 255) + mask_len = 255; + if (mask_len) + strncpy(mask, lastname, mask_len); + } + mask_len = strnlen(mask, mask_len); + VERBOSE("new mask, len=%d@%d of %d, mask=%.*s\n", + mask_len, ff_lastname, req->rq_ldata, mask_len, mask); + + first = 0; + loop_count = 0; + } + +out_free: + smb_rput(req); +out_name: + kfree(name_buf); +out: + unlock_kernel(); + return result; +} + +/* + * This version uses the trans2 TRANSACT2_FINDFIRST message + * to get the attribute data. + * + * Bugs Noted: + */ +static int +smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, + struct smb_fattr *fattr) +{ + char *param, *mask; + __u16 date, time; + int mask_len, result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + param = req->rq_buffer; + mask = param + 12; + + mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dentry,NULL); + if (mask_len < 0) { + result = mask_len; + goto out_free; + } + VERBOSE("name=%s, len=%d\n", mask, mask_len); + WSET(param, 0, aSYSTEM | aHIDDEN | aDIR); + WSET(param, 2, 1); /* max count */ + WSET(param, 4, 1); /* close after this call */ + WSET(param, 6, 1); /* info_level */ + DSET(param, 8, 0); + + req->rq_trans2_command = TRANSACT2_FINDFIRST; + req->rq_ldata = 0; + req->rq_data = NULL; + req->rq_lparm = 12 + mask_len; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) + goto out_free; + if (req->rq_rcls != 0) { + result = smb_errno(req); +#ifdef SMBFS_PARANOIA + if (result != -ENOENT) + PARANOIA("error for %s, rcls=%d, err=%d\n", + mask, req->rq_rcls, req->rq_err); +#endif + goto out_free; + } + /* Make sure we got enough data ... */ + result = -EINVAL; + if (req->rq_ldata < 22 || WVAL(req->rq_parm, 2) != 1) { + PARANOIA("bad result for %s, len=%d, count=%d\n", + mask, req->rq_ldata, WVAL(req->rq_parm, 2)); + goto out_free; + } + + /* + * Decode the response into the fattr ... + */ + date = WVAL(req->rq_data, 0); + time = WVAL(req->rq_data, 2); + fattr->f_ctime.tv_sec = date_dos2unix(server, date, time); + fattr->f_ctime.tv_nsec = 0; + + date = WVAL(req->rq_data, 4); + time = WVAL(req->rq_data, 6); + fattr->f_atime.tv_sec = date_dos2unix(server, date, time); + fattr->f_atime.tv_nsec = 0; + + date = WVAL(req->rq_data, 8); + time = WVAL(req->rq_data, 10); + fattr->f_mtime.tv_sec = date_dos2unix(server, date, time); + fattr->f_mtime.tv_nsec = 0; + VERBOSE("name=%s, date=%x, time=%x, mtime=%ld\n", + mask, date, time, fattr->f_mtime.tv_sec); + fattr->f_size = DVAL(req->rq_data, 12); + /* ULONG allocation size */ + fattr->attr = WVAL(req->rq_data, 20); + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +static int +smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *fattr) +{ + int result; + char *p; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + p = smb_setup_header(req, SMBgetatr, 0, 0); + result = smb_simple_encode_path(req, &p, dir, NULL); + if (result < 0) + goto out_free; + smb_setup_bcc(req, p); + + if ((result = smb_request_ok(req, SMBgetatr, 10, 0)) < 0) + goto out_free; + fattr->attr = WVAL(req->rq_header, smb_vwv0); + fattr->f_mtime.tv_sec = local2utc(server, DVAL(req->rq_header, smb_vwv1)); + fattr->f_mtime.tv_nsec = 0; + fattr->f_size = DVAL(req->rq_header, smb_vwv3); + fattr->f_ctime = fattr->f_mtime; + fattr->f_atime = fattr->f_mtime; +#ifdef SMBFS_DEBUG_TIMESTAMP + printk("getattr_core: %s/%s, mtime=%ld\n", + DENTRY_PATH(dir), fattr->f_mtime); +#endif + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +/* + * Bugs Noted: + * (1) Win 95 swaps the date and time fields in the standard info level. + */ +static int +smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, + struct smb_request *req, int infolevel) +{ + char *p, *param; + int result; + + param = req->rq_buffer; + WSET(param, 0, infolevel); + DSET(param, 2, 0); + result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL); + if (result < 0) + goto out; + p = param + 6 + result; + + req->rq_trans2_command = TRANSACT2_QPATHINFO; + req->rq_ldata = 0; + req->rq_data = NULL; + req->rq_lparm = p - param; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) + goto out; + if (req->rq_rcls != 0) { + VERBOSE("for %s: result=%d, rcls=%d, err=%d\n", + ¶m[6], result, req->rq_rcls, req->rq_err); + result = smb_errno(req); + goto out; + } + result = -ENOENT; + if (req->rq_ldata < 22) { + PARANOIA("not enough data for %s, len=%d\n", + ¶m[6], req->rq_ldata); + goto out; + } + + result = 0; +out: + return result; +} + +static int +smb_proc_getattr_trans2_std(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *attr) +{ + u16 date, time; + int off_date = 0, off_time = 2; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + result = smb_proc_getattr_trans2(server, dir, req, SMB_INFO_STANDARD); + if (result < 0) + goto out_free; + + /* + * Kludge alert: Win 95 swaps the date and time field, + * contrary to the CIFS docs and Win NT practice. + */ + if (server->mnt->flags & SMB_MOUNT_WIN95) { + off_date = 2; + off_time = 0; + } + date = WVAL(req->rq_data, off_date); + time = WVAL(req->rq_data, off_time); + attr->f_ctime.tv_sec = date_dos2unix(server, date, time); + attr->f_ctime.tv_nsec = 0; + + date = WVAL(req->rq_data, 4 + off_date); + time = WVAL(req->rq_data, 4 + off_time); + attr->f_atime.tv_sec = date_dos2unix(server, date, time); + attr->f_atime.tv_nsec = 0; + + date = WVAL(req->rq_data, 8 + off_date); + time = WVAL(req->rq_data, 8 + off_time); + attr->f_mtime.tv_sec = date_dos2unix(server, date, time); + attr->f_mtime.tv_nsec = 0; +#ifdef SMBFS_DEBUG_TIMESTAMP + printk(KERN_DEBUG "getattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", + DENTRY_PATH(dir), date, time, attr->f_mtime); +#endif + attr->f_size = DVAL(req->rq_data, 12); + attr->attr = WVAL(req->rq_data, 20); + +out_free: + smb_rput(req); +out: + return result; +} + +static int +smb_proc_getattr_trans2_all(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *attr) +{ + struct smb_request *req; + int result; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + result = smb_proc_getattr_trans2(server, dir, req, + SMB_QUERY_FILE_ALL_INFO); + if (result < 0) + goto out_free; + + attr->f_ctime = smb_ntutc2unixutc(LVAL(req->rq_data, 0)); + attr->f_atime = smb_ntutc2unixutc(LVAL(req->rq_data, 8)); + attr->f_mtime = smb_ntutc2unixutc(LVAL(req->rq_data, 16)); + /* change (24) */ + attr->attr = WVAL(req->rq_data, 32); + /* pad? (34) */ + /* allocated size (40) */ + attr->f_size = LVAL(req->rq_data, 48); + +out_free: + smb_rput(req); +out: + return result; +} + +static int +smb_proc_getattr_unix(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *attr) +{ + struct smb_request *req; + int result; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + result = smb_proc_getattr_trans2(server, dir, req, + SMB_QUERY_FILE_UNIX_BASIC); + if (result < 0) + goto out_free; + + smb_decode_unix_basic(attr, server, req->rq_data); + +out_free: + smb_rput(req); +out: + return result; +} + +static int +smb_proc_getattr_95(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *attr) +{ + struct inode *inode = dir->d_inode; + int result; + + /* FIXME: why not use the "all" version? */ + result = smb_proc_getattr_trans2_std(server, dir, attr); + if (result < 0) + goto out; + + /* + * None of the getattr versions here can make win9x return the right + * filesize if there are changes made to an open file. + * A seek-to-end does return the right size, but we only need to do + * that on files we have written. + */ + if (inode && SMB_I(inode)->flags & SMB_F_LOCALWRITE && + smb_is_open(inode)) + { + __u16 fileid = SMB_I(inode)->fileid; + attr->f_size = smb_proc_seek(server, fileid, 2, 0); + } + +out: + return result; +} + +static int +smb_proc_ops_wait(struct smb_sb_info *server) +{ + int result; + + result = wait_event_interruptible_timeout(server->conn_wq, + server->conn_complete, 30*HZ); + + if (!result || signal_pending(current)) + return -EIO; + + return 0; +} + +static int +smb_proc_getattr_null(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *fattr) +{ + int result; + + if (smb_proc_ops_wait(server) < 0) + return -EIO; + + smb_init_dirent(server, fattr); + result = server->ops->getattr(server, dir, fattr); + smb_finish_dirent(server, fattr); + + return result; +} + +static int +smb_proc_readdir_null(struct file *filp, void *dirent, filldir_t filldir, + struct smb_cache_control *ctl) +{ + struct smb_sb_info *server = server_from_dentry(filp->f_path.dentry); + + if (smb_proc_ops_wait(server) < 0) + return -EIO; + + return server->ops->readdir(filp, dirent, filldir, ctl); +} + +int +smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr) +{ + struct smb_sb_info *server = server_from_dentry(dir); + int result; + + smb_init_dirent(server, fattr); + result = server->ops->getattr(server, dir, fattr); + smb_finish_dirent(server, fattr); + + return result; +} + + +/* + * Because of bugs in the core protocol, we use this only to set + * attributes. See smb_proc_settime() below for timestamp handling. + * + * Bugs Noted: + * (1) If mtime is non-zero, both Win 3.1 and Win 95 fail + * with an undocumented error (ERRDOS code 50). Setting + * mtime to 0 allows the attributes to be set. + * (2) The extra parameters following the name string aren't + * in the CIFS docs, but seem to be necessary for operation. + */ +static int +smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, + __u16 attr) +{ + char *p; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + p = smb_setup_header(req, SMBsetatr, 8, 0); + WSET(req->rq_header, smb_vwv0, attr); + DSET(req->rq_header, smb_vwv1, 0); /* mtime */ + WSET(req->rq_header, smb_vwv3, 0); /* reserved values */ + WSET(req->rq_header, smb_vwv4, 0); + WSET(req->rq_header, smb_vwv5, 0); + WSET(req->rq_header, smb_vwv6, 0); + WSET(req->rq_header, smb_vwv7, 0); + result = smb_simple_encode_path(req, &p, dentry, NULL); + if (result < 0) + goto out_free; + if (p + 2 > (char *)req->rq_buffer + req->rq_bufsize) { + result = -ENAMETOOLONG; + goto out_free; + } + *p++ = 4; + *p++ = 0; + smb_setup_bcc(req, p); + + result = smb_request_ok(req, SMBsetatr, 0, 0); + if (result < 0) + goto out_free; + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +/* + * Because of bugs in the trans2 setattr messages, we must set + * attributes and timestamps separately. The core SMBsetatr + * message seems to be the only reliable way to set attributes. + */ +int +smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr) +{ + struct smb_sb_info *server = server_from_dentry(dir); + int result; + + VERBOSE("setting %s/%s, open=%d\n", + DENTRY_PATH(dir), smb_is_open(dir->d_inode)); + result = smb_proc_setattr_core(server, dir, fattr->attr); + return result; +} + +/* + * Sets the timestamps for an file open with write permissions. + */ +static int +smb_proc_setattr_ext(struct smb_sb_info *server, + struct inode *inode, struct smb_fattr *fattr) +{ + __u16 date, time; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + smb_setup_header(req, SMBsetattrE, 7, 0); + WSET(req->rq_header, smb_vwv0, SMB_I(inode)->fileid); + /* We don't change the creation time */ + WSET(req->rq_header, smb_vwv1, 0); + WSET(req->rq_header, smb_vwv2, 0); + date_unix2dos(server, fattr->f_atime.tv_sec, &date, &time); + WSET(req->rq_header, smb_vwv3, date); + WSET(req->rq_header, smb_vwv4, time); + date_unix2dos(server, fattr->f_mtime.tv_sec, &date, &time); + WSET(req->rq_header, smb_vwv5, date); + WSET(req->rq_header, smb_vwv6, time); +#ifdef SMBFS_DEBUG_TIMESTAMP + printk(KERN_DEBUG "smb_proc_setattr_ext: date=%d, time=%d, mtime=%ld\n", + date, time, fattr->f_mtime); +#endif + + req->rq_flags |= SMB_REQ_NORETRY; + result = smb_request_ok(req, SMBsetattrE, 0, 0); + if (result < 0) + goto out_free; + result = 0; +out_free: + smb_rput(req); +out: + return result; +} + +/* + * Bugs Noted: + * (1) The TRANSACT2_SETPATHINFO message under Win NT 4.0 doesn't + * set the file's attribute flags. + */ +static int +smb_proc_setattr_trans2(struct smb_sb_info *server, + struct dentry *dir, struct smb_fattr *fattr) +{ + __u16 date, time; + char *p, *param; + int result; + char data[26]; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + param = req->rq_buffer; + + WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ + DSET(param, 2, 0); + result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL); + if (result < 0) + goto out_free; + p = param + 6 + result; + + WSET(data, 0, 0); /* creation time */ + WSET(data, 2, 0); + date_unix2dos(server, fattr->f_atime.tv_sec, &date, &time); + WSET(data, 4, date); + WSET(data, 6, time); + date_unix2dos(server, fattr->f_mtime.tv_sec, &date, &time); + WSET(data, 8, date); + WSET(data, 10, time); +#ifdef SMBFS_DEBUG_TIMESTAMP + printk(KERN_DEBUG "setattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", + DENTRY_PATH(dir), date, time, fattr->f_mtime); +#endif + DSET(data, 12, 0); /* size */ + DSET(data, 16, 0); /* blksize */ + WSET(data, 20, 0); /* attr */ + DSET(data, 22, 0); /* ULONG EA size */ + + req->rq_trans2_command = TRANSACT2_SETPATHINFO; + req->rq_ldata = 26; + req->rq_data = data; + req->rq_lparm = p - param; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) + goto out_free; + result = 0; + if (req->rq_rcls != 0) + result = smb_errno(req); + +out_free: + smb_rput(req); +out: + return result; +} + +/* + * ATTR_MODE 0x001 + * ATTR_UID 0x002 + * ATTR_GID 0x004 + * ATTR_SIZE 0x008 + * ATTR_ATIME 0x010 + * ATTR_MTIME 0x020 + * ATTR_CTIME 0x040 + * ATTR_ATIME_SET 0x080 + * ATTR_MTIME_SET 0x100 + * ATTR_FORCE 0x200 + * ATTR_ATTR_FLAG 0x400 + * + * major/minor should only be set by mknod. + */ +int +smb_proc_setattr_unix(struct dentry *d, struct iattr *attr, + unsigned int major, unsigned int minor) +{ + struct smb_sb_info *server = server_from_dentry(d); + u64 nttime; + char *p, *param; + int result; + char data[100]; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + param = req->rq_buffer; + + DEBUG1("valid flags = 0x%04x\n", attr->ia_valid); + + WSET(param, 0, SMB_SET_FILE_UNIX_BASIC); + DSET(param, 2, 0); + result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, d, NULL); + if (result < 0) + goto out_free; + p = param + 6 + result; + + /* 0 L file size in bytes */ + /* 8 L file size on disk in bytes (block count) */ + /* 40 L uid */ + /* 48 L gid */ + /* 56 W file type enum */ + /* 60 L devmajor */ + /* 68 L devminor */ + /* 76 L unique ID (inode) */ + /* 84 L permissions */ + /* 92 L link count */ + LSET(data, 0, SMB_SIZE_NO_CHANGE); + LSET(data, 8, SMB_SIZE_NO_CHANGE); + LSET(data, 16, SMB_TIME_NO_CHANGE); + LSET(data, 24, SMB_TIME_NO_CHANGE); + LSET(data, 32, SMB_TIME_NO_CHANGE); + LSET(data, 40, SMB_UID_NO_CHANGE); + LSET(data, 48, SMB_GID_NO_CHANGE); + DSET(data, 56, smb_filetype_from_mode(attr->ia_mode)); + LSET(data, 60, major); + LSET(data, 68, minor); + LSET(data, 76, 0); + LSET(data, 84, SMB_MODE_NO_CHANGE); + LSET(data, 92, 0); + + if (attr->ia_valid & ATTR_SIZE) { + LSET(data, 0, attr->ia_size); + LSET(data, 8, 0); /* can't set anyway */ + } + + /* + * FIXME: check the conversion function it the correct one + * + * we can't set ctime but we might as well pass this to the server + * and let it ignore it. + */ + if (attr->ia_valid & ATTR_CTIME) { + nttime = smb_unixutc2ntutc(attr->ia_ctime); + LSET(data, 16, nttime); + } + if (attr->ia_valid & ATTR_ATIME) { + nttime = smb_unixutc2ntutc(attr->ia_atime); + LSET(data, 24, nttime); + } + if (attr->ia_valid & ATTR_MTIME) { + nttime = smb_unixutc2ntutc(attr->ia_mtime); + LSET(data, 32, nttime); + } + + if (attr->ia_valid & ATTR_UID) { + LSET(data, 40, attr->ia_uid); + } + if (attr->ia_valid & ATTR_GID) { + LSET(data, 48, attr->ia_gid); + } + + if (attr->ia_valid & ATTR_MODE) { + LSET(data, 84, attr->ia_mode); + } + + req->rq_trans2_command = TRANSACT2_SETPATHINFO; + req->rq_ldata = 100; + req->rq_data = data; + req->rq_lparm = p - param; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + +out_free: + smb_rput(req); +out: + return result; +} + + +/* + * Set the modify and access timestamps for a file. + * + * Incredibly enough, in all of SMB there is no message to allow + * setting both attributes and timestamps at once. + * + * Bugs Noted: + * (1) Win 95 doesn't support the TRANSACT2_SETFILEINFO message + * with info level 1 (INFO_STANDARD). + * (2) Win 95 seems not to support setting directory timestamps. + * (3) Under the core protocol apparently the only way to set the + * timestamp is to open and close the file. + */ +int +smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + struct inode *inode = dentry->d_inode; + int result; + + VERBOSE("setting %s/%s, open=%d\n", + DENTRY_PATH(dentry), smb_is_open(inode)); + + /* setting the time on a Win95 server fails (tridge) */ + if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 && + !(server->mnt->flags & SMB_MOUNT_WIN95)) { + if (smb_is_open(inode) && SMB_I(inode)->access != SMB_O_RDONLY) + result = smb_proc_setattr_ext(server, inode, fattr); + else + result = smb_proc_setattr_trans2(server, dentry, fattr); + } else { + /* + * Fail silently on directories ... timestamp can't be set? + */ + result = 0; + if (S_ISREG(inode->i_mode)) { + /* + * Set the mtime by opening and closing the file. + * Note that the file is opened read-only, but this + * still allows us to set the date (tridge) + */ + result = -EACCES; + if (!smb_is_open(inode)) + smb_proc_open(server, dentry, SMB_O_RDONLY); + if (smb_is_open(inode)) { + inode->i_mtime = fattr->f_mtime; + result = smb_proc_close_inode(server, inode); + } + } + } + + return result; +} + +int +smb_proc_dskattr(struct dentry *dentry, struct kstatfs *attr) +{ + struct smb_sb_info *server = SMB_SB(dentry->d_sb); + int result; + char *p; + long unit; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + smb_setup_header(req, SMBdskattr, 0, 0); + if ((result = smb_request_ok(req, SMBdskattr, 5, 0)) < 0) + goto out_free; + p = SMB_VWV(req->rq_header); + unit = (WVAL(p, 2) * WVAL(p, 4)) >> SMB_ST_BLKSHIFT; + attr->f_blocks = WVAL(p, 0) * unit; + attr->f_bsize = SMB_ST_BLKSIZE; + attr->f_bavail = attr->f_bfree = WVAL(p, 6) * unit; + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +int +smb_proc_read_link(struct smb_sb_info *server, struct dentry *d, + char *buffer, int len) +{ + char *p, *param; + int result; + struct smb_request *req; + + DEBUG1("readlink of %s/%s\n", DENTRY_PATH(d)); + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + param = req->rq_buffer; + + WSET(param, 0, SMB_QUERY_FILE_UNIX_LINK); + DSET(param, 2, 0); + result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, d, NULL); + if (result < 0) + goto out_free; + p = param + 6 + result; + + req->rq_trans2_command = TRANSACT2_QPATHINFO; + req->rq_ldata = 0; + req->rq_data = NULL; + req->rq_lparm = p - param; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) + goto out_free; + DEBUG1("for %s: result=%d, rcls=%d, err=%d\n", + ¶m[6], result, req->rq_rcls, req->rq_err); + + /* copy data up to the \0 or buffer length */ + result = len; + if (req->rq_ldata < len) + result = req->rq_ldata; + strncpy(buffer, req->rq_data, result); + +out_free: + smb_rput(req); +out: + return result; +} + + +/* + * Create a symlink object called dentry which points to oldpath. + * Samba does not permit dangling links but returns a suitable error message. + */ +int +smb_proc_symlink(struct smb_sb_info *server, struct dentry *d, + const char *oldpath) +{ + char *p, *param; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + param = req->rq_buffer; + + WSET(param, 0, SMB_SET_FILE_UNIX_LINK); + DSET(param, 2, 0); + result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+1, d, NULL); + if (result < 0) + goto out_free; + p = param + 6 + result; + + req->rq_trans2_command = TRANSACT2_SETPATHINFO; + req->rq_ldata = strlen(oldpath) + 1; + req->rq_data = (char *) oldpath; + req->rq_lparm = p - param; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) + goto out_free; + + DEBUG1("for %s: result=%d, rcls=%d, err=%d\n", + ¶m[6], result, req->rq_rcls, req->rq_err); + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +/* + * Create a hard link object called new_dentry which points to dentry. + */ +int +smb_proc_link(struct smb_sb_info *server, struct dentry *dentry, + struct dentry *new_dentry) +{ + char *p, *param; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + param = req->rq_buffer; + + WSET(param, 0, SMB_SET_FILE_UNIX_HLINK); + DSET(param, 2, 0); + result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+1, + new_dentry, NULL); + if (result < 0) + goto out_free; + p = param + 6 + result; + + /* Grr, pointless separation of parameters and data ... */ + req->rq_data = p; + req->rq_ldata = smb_encode_path(server, p, SMB_MAXPATHLEN+1, + dentry, NULL); + + req->rq_trans2_command = TRANSACT2_SETPATHINFO; + req->rq_lparm = p - param; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) + goto out_free; + + DEBUG1("for %s: result=%d, rcls=%d, err=%d\n", + ¶m[6], result, req->rq_rcls, req->rq_err); + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +static int +smb_proc_query_cifsunix(struct smb_sb_info *server) +{ + int result; + int major, minor; + u64 caps; + char param[2]; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 100))) + goto out; + + WSET(param, 0, SMB_QUERY_CIFS_UNIX_INFO); + + req->rq_trans2_command = TRANSACT2_QFSINFO; + req->rq_ldata = 0; + req->rq_data = NULL; + req->rq_lparm = 2; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) + goto out_free; + + if (req->rq_ldata < 12) { + PARANOIA("Not enough data\n"); + goto out_free; + } + major = WVAL(req->rq_data, 0); + minor = WVAL(req->rq_data, 2); + + DEBUG1("Server implements CIFS Extensions for UNIX systems v%d.%d\n", + major, minor); + /* FIXME: verify that we are ok with this major/minor? */ + + caps = LVAL(req->rq_data, 4); + DEBUG1("Server capabilities 0x%016llx\n", caps); + +out_free: + smb_rput(req); +out: + return result; +} + + +static void +install_ops(struct smb_ops *dst, struct smb_ops *src) +{ + memcpy(dst, src, sizeof(void *) * SMB_OPS_NUM_STATIC); +} + +/* < LANMAN2 */ +static struct smb_ops smb_ops_core = +{ + .read = smb_proc_read, + .write = smb_proc_write, + .readdir = smb_proc_readdir_short, + .getattr = smb_proc_getattr_core, + .truncate = smb_proc_trunc32, +}; + +/* LANMAN2, OS/2, others? */ +static struct smb_ops smb_ops_os2 = +{ + .read = smb_proc_read, + .write = smb_proc_write, + .readdir = smb_proc_readdir_long, + .getattr = smb_proc_getattr_trans2_std, + .truncate = smb_proc_trunc32, +}; + +/* Win95, and possibly some NetApp versions too */ +static struct smb_ops smb_ops_win95 = +{ + .read = smb_proc_read, /* does not support 12word readX */ + .write = smb_proc_write, + .readdir = smb_proc_readdir_long, + .getattr = smb_proc_getattr_95, + .truncate = smb_proc_trunc95, +}; + +/* Samba, NT4 and NT5 */ +static struct smb_ops smb_ops_winNT = +{ + .read = smb_proc_readX, + .write = smb_proc_writeX, + .readdir = smb_proc_readdir_long, + .getattr = smb_proc_getattr_trans2_all, + .truncate = smb_proc_trunc64, +}; + +/* Samba w/ unix extensions. Others? */ +static struct smb_ops smb_ops_unix = +{ + .read = smb_proc_readX, + .write = smb_proc_writeX, + .readdir = smb_proc_readdir_long, + .getattr = smb_proc_getattr_unix, + /* FIXME: core/ext/time setattr needs to be cleaned up! */ + /* .setattr = smb_proc_setattr_unix, */ + .truncate = smb_proc_trunc64, +}; + +/* Place holder until real ops are in place */ +static struct smb_ops smb_ops_null = +{ + .readdir = smb_proc_readdir_null, + .getattr = smb_proc_getattr_null, +}; + +void smb_install_null_ops(struct smb_ops *ops) +{ + install_ops(ops, &smb_ops_null); +} diff --git a/drivers/staging/smbfs/proto.h b/drivers/staging/smbfs/proto.h new file mode 100644 index 000000000000..05939a6f43e6 --- /dev/null +++ b/drivers/staging/smbfs/proto.h @@ -0,0 +1,87 @@ +/* + * Autogenerated with cproto on: Sat Sep 13 17:18:51 CEST 2003 + */ + +struct smb_request; +struct sock; +struct statfs; + +/* proc.c */ +extern int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp); +extern __u32 smb_len(__u8 *p); +extern int smb_get_rsize(struct smb_sb_info *server); +extern int smb_get_wsize(struct smb_sb_info *server); +extern int smb_errno(struct smb_request *req); +extern int smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt); +extern __u8 *smb_setup_header(struct smb_request *req, __u8 command, __u16 wct, __u16 bcc); +extern int smb_open(struct dentry *dentry, int wish); +extern int smb_close(struct inode *ino); +extern int smb_close_fileid(struct dentry *dentry, __u16 fileid); +extern int smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid); +extern int smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry); +extern int smb_proc_mkdir(struct dentry *dentry); +extern int smb_proc_rmdir(struct dentry *dentry); +extern int smb_proc_unlink(struct dentry *dentry); +extern int smb_proc_flush(struct smb_sb_info *server, __u16 fileid); +extern void smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr, + struct super_block *sb); +extern int smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr); +extern int smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr); +extern int smb_proc_setattr_unix(struct dentry *d, struct iattr *attr, unsigned int major, unsigned int minor); +extern int smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr); +extern int smb_proc_dskattr(struct dentry *dentry, struct kstatfs *attr); +extern int smb_proc_read_link(struct smb_sb_info *server, struct dentry *d, char *buffer, int len); +extern int smb_proc_symlink(struct smb_sb_info *server, struct dentry *d, const char *oldpath); +extern int smb_proc_link(struct smb_sb_info *server, struct dentry *dentry, struct dentry *new_dentry); +extern void smb_install_null_ops(struct smb_ops *ops); +/* dir.c */ +extern const struct file_operations smb_dir_operations; +extern const struct inode_operations smb_dir_inode_operations; +extern const struct inode_operations smb_dir_inode_operations_unix; +extern void smb_new_dentry(struct dentry *dentry); +extern void smb_renew_times(struct dentry *dentry); +/* cache.c */ +extern void smb_invalid_dir_cache(struct inode *dir); +extern void smb_invalidate_dircache_entries(struct dentry *parent); +extern struct dentry *smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos); +extern int smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctrl, struct qstr *qname, struct smb_fattr *entry); +/* sock.c */ +extern void smb_data_ready(struct sock *sk, int len); +extern int smb_valid_socket(struct inode *inode); +extern void smb_close_socket(struct smb_sb_info *server); +extern int smb_recv_available(struct smb_sb_info *server); +extern int smb_receive_header(struct smb_sb_info *server); +extern int smb_receive_drop(struct smb_sb_info *server); +extern int smb_receive(struct smb_sb_info *server, struct smb_request *req); +extern int smb_send_request(struct smb_request *req); +/* inode.c */ +extern struct inode *smb_iget(struct super_block *sb, struct smb_fattr *fattr); +extern void smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr); +extern void smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr); +extern void smb_invalidate_inodes(struct smb_sb_info *server); +extern int smb_revalidate_inode(struct dentry *dentry); +extern int smb_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); +extern int smb_notify_change(struct dentry *dentry, struct iattr *attr); +/* file.c */ +extern const struct address_space_operations smb_file_aops; +extern const struct file_operations smb_file_operations; +extern const struct inode_operations smb_file_inode_operations; +/* ioctl.c */ +extern long smb_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +/* smbiod.c */ +extern void smbiod_wake_up(void); +extern int smbiod_register_server(struct smb_sb_info *server); +extern void smbiod_unregister_server(struct smb_sb_info *server); +extern void smbiod_flush(struct smb_sb_info *server); +extern int smbiod_retry(struct smb_sb_info *server); +/* request.c */ +extern int smb_init_request_cache(void); +extern void smb_destroy_request_cache(void); +extern struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize); +extern void smb_rput(struct smb_request *req); +extern int smb_add_request(struct smb_request *req); +extern int smb_request_send_server(struct smb_sb_info *server); +extern int smb_request_recv(struct smb_sb_info *server); +/* symlink.c */ +extern int smb_symlink(struct inode *inode, struct dentry *dentry, const char *oldname); +extern const struct inode_operations smb_link_inode_operations; diff --git a/drivers/staging/smbfs/request.c b/drivers/staging/smbfs/request.c new file mode 100644 index 000000000000..3e7716864306 --- /dev/null +++ b/drivers/staging/smbfs/request.c @@ -0,0 +1,817 @@ +/* + * request.c + * + * Copyright (C) 2001 by Urban Widmark + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include +#include +#include +#include +#include +#include + +#include "smb_fs.h" +#include "smbno.h" +#include "smb_mount.h" +#include "smb_debug.h" +#include "request.h" +#include "proto.h" + +/* #define SMB_SLAB_DEBUG (SLAB_RED_ZONE | SLAB_POISON) */ +#define SMB_SLAB_DEBUG 0 + +/* cache for request structures */ +static struct kmem_cache *req_cachep; + +static int smb_request_send_req(struct smb_request *req); + +/* + /proc/slabinfo: + name, active, num, objsize, active_slabs, num_slaps, #pages +*/ + + +int smb_init_request_cache(void) +{ + req_cachep = kmem_cache_create("smb_request", + sizeof(struct smb_request), 0, + SMB_SLAB_DEBUG | SLAB_HWCACHE_ALIGN, + NULL); + if (req_cachep == NULL) + return -ENOMEM; + + return 0; +} + +void smb_destroy_request_cache(void) +{ + kmem_cache_destroy(req_cachep); +} + +/* + * Allocate and initialise a request structure + */ +static struct smb_request *smb_do_alloc_request(struct smb_sb_info *server, + int bufsize) +{ + struct smb_request *req; + unsigned char *buf = NULL; + + req = kmem_cache_zalloc(req_cachep, GFP_KERNEL); + VERBOSE("allocating request: %p\n", req); + if (!req) + goto out; + + if (bufsize > 0) { + buf = kmalloc(bufsize, GFP_NOFS); + if (!buf) { + kmem_cache_free(req_cachep, req); + return NULL; + } + } + + req->rq_buffer = buf; + req->rq_bufsize = bufsize; + req->rq_server = server; + init_waitqueue_head(&req->rq_wait); + INIT_LIST_HEAD(&req->rq_queue); + atomic_set(&req->rq_count, 1); + +out: + return req; +} + +struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize) +{ + struct smb_request *req = NULL; + + for (;;) { + atomic_inc(&server->nr_requests); + if (atomic_read(&server->nr_requests) <= MAX_REQUEST_HARD) { + req = smb_do_alloc_request(server, bufsize); + if (req != NULL) + break; + } + +#if 0 + /* + * Try to free up at least one request in order to stay + * below the hard limit + */ + if (nfs_try_to_free_pages(server)) + continue; + + if (fatal_signal_pending(current)) + return ERR_PTR(-ERESTARTSYS); + current->policy = SCHED_YIELD; + schedule(); +#else + /* FIXME: we want something like nfs does above, but that + requires changes to all callers and can wait. */ + break; +#endif + } + return req; +} + +static void smb_free_request(struct smb_request *req) +{ + atomic_dec(&req->rq_server->nr_requests); + if (req->rq_buffer && !(req->rq_flags & SMB_REQ_STATIC)) + kfree(req->rq_buffer); + kfree(req->rq_trans2buffer); + kmem_cache_free(req_cachep, req); +} + +/* + * What prevents a rget to race with a rput? The count must never drop to zero + * while it is in use. Only rput if it is ok that it is free'd. + */ +static void smb_rget(struct smb_request *req) +{ + atomic_inc(&req->rq_count); +} +void smb_rput(struct smb_request *req) +{ + if (atomic_dec_and_test(&req->rq_count)) { + list_del_init(&req->rq_queue); + smb_free_request(req); + } +} + +/* setup to receive the data part of the SMB */ +static int smb_setup_bcc(struct smb_request *req) +{ + int result = 0; + req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd; + + if (req->rq_rlen > req->rq_bufsize) { + PARANOIA("Packet too large %d > %d\n", + req->rq_rlen, req->rq_bufsize); + return -ENOBUFS; + } + + req->rq_iov[0].iov_base = req->rq_buffer; + req->rq_iov[0].iov_len = req->rq_rlen; + req->rq_iovlen = 1; + + return result; +} + +/* + * Prepare a "normal" request structure. + */ +static int smb_setup_request(struct smb_request *req) +{ + int len = smb_len(req->rq_header) + 4; + req->rq_slen = len; + + /* if we expect a data part in the reply we set the iov's to read it */ + if (req->rq_resp_bcc) + req->rq_setup_read = smb_setup_bcc; + + /* This tries to support re-using the same request */ + req->rq_bytes_sent = 0; + req->rq_rcls = 0; + req->rq_err = 0; + req->rq_errno = 0; + req->rq_fragment = 0; + kfree(req->rq_trans2buffer); + req->rq_trans2buffer = NULL; + + return 0; +} + +/* + * Prepare a transaction2 request structure + */ +static int smb_setup_trans2request(struct smb_request *req) +{ + struct smb_sb_info *server = req->rq_server; + int mparam, mdata; + static unsigned char padding[4]; + + /* I know the following is very ugly, but I want to build the + smb packet as efficiently as possible. */ + + const int smb_parameters = 15; + const int header = SMB_HEADER_LEN + 2 * smb_parameters + 2; + const int oparam = ALIGN(header + 3, sizeof(u32)); + const int odata = ALIGN(oparam + req->rq_lparm, sizeof(u32)); + const int bcc = (req->rq_data ? odata + req->rq_ldata : + oparam + req->rq_lparm) - header; + + if ((bcc + oparam) > server->opt.max_xmit) + return -ENOMEM; + smb_setup_header(req, SMBtrans2, smb_parameters, bcc); + + /* + * max parameters + max data + max setup == bufsize to make NT4 happy + * and not abort the transfer or split into multiple responses. It also + * makes smbfs happy as handling packets larger than the buffer size + * is extra work. + * + * OS/2 is probably going to hate me for this ... + */ + mparam = SMB_TRANS2_MAX_PARAM; + mdata = req->rq_bufsize - mparam; + + mdata = server->opt.max_xmit - mparam - 100; + if (mdata < 1024) { + mdata = 1024; + mparam = 20; + } + +#if 0 + /* NT/win2k has ~4k max_xmit, so with this we request more than it wants + to return as one SMB. Useful for testing the fragmented trans2 + handling. */ + mdata = 8192; +#endif + + WSET(req->rq_header, smb_tpscnt, req->rq_lparm); + WSET(req->rq_header, smb_tdscnt, req->rq_ldata); + WSET(req->rq_header, smb_mprcnt, mparam); + WSET(req->rq_header, smb_mdrcnt, mdata); + WSET(req->rq_header, smb_msrcnt, 0); /* max setup always 0 ? */ + WSET(req->rq_header, smb_flags, 0); + DSET(req->rq_header, smb_timeout, 0); + WSET(req->rq_header, smb_pscnt, req->rq_lparm); + WSET(req->rq_header, smb_psoff, oparam - 4); + WSET(req->rq_header, smb_dscnt, req->rq_ldata); + WSET(req->rq_header, smb_dsoff, req->rq_data ? odata - 4 : 0); + *(req->rq_header + smb_suwcnt) = 0x01; /* setup count */ + *(req->rq_header + smb_suwcnt + 1) = 0x00; /* reserved */ + WSET(req->rq_header, smb_setup0, req->rq_trans2_command); + + req->rq_iovlen = 2; + req->rq_iov[0].iov_base = (void *) req->rq_header; + req->rq_iov[0].iov_len = oparam; + req->rq_iov[1].iov_base = (req->rq_parm==NULL) ? padding : req->rq_parm; + req->rq_iov[1].iov_len = req->rq_lparm; + req->rq_slen = oparam + req->rq_lparm; + + if (req->rq_data) { + req->rq_iovlen += 2; + req->rq_iov[2].iov_base = padding; + req->rq_iov[2].iov_len = odata - oparam - req->rq_lparm; + req->rq_iov[3].iov_base = req->rq_data; + req->rq_iov[3].iov_len = req->rq_ldata; + req->rq_slen = odata + req->rq_ldata; + } + + /* always a data part for trans2 replies */ + req->rq_setup_read = smb_setup_bcc; + + return 0; +} + +/* + * Add a request and tell smbiod to process it + */ +int smb_add_request(struct smb_request *req) +{ + long timeleft; + struct smb_sb_info *server = req->rq_server; + int result = 0; + + smb_setup_request(req); + if (req->rq_trans2_command) { + if (req->rq_buffer == NULL) { + PARANOIA("trans2 attempted without response buffer!\n"); + return -EIO; + } + result = smb_setup_trans2request(req); + } + if (result < 0) + return result; + +#ifdef SMB_DEBUG_PACKET_SIZE + add_xmit_stats(req); +#endif + + /* add 'req' to the queue of requests */ + if (smb_lock_server_interruptible(server)) + return -EINTR; + + /* + * Try to send the request as the process. If that fails we queue the + * request and let smbiod send it later. + */ + + /* FIXME: each server has a number on the maximum number of parallel + requests. 10, 50 or so. We should not allow more requests to be + active. */ + if (server->mid > 0xf000) + server->mid = 0; + req->rq_mid = server->mid++; + WSET(req->rq_header, smb_mid, req->rq_mid); + + result = 0; + if (server->state == CONN_VALID) { + if (list_empty(&server->xmitq)) + result = smb_request_send_req(req); + if (result < 0) { + /* Connection lost? */ + server->conn_error = result; + server->state = CONN_INVALID; + } + } + if (result != 1) + list_add_tail(&req->rq_queue, &server->xmitq); + smb_rget(req); + + if (server->state != CONN_VALID) + smbiod_retry(server); + + smb_unlock_server(server); + + smbiod_wake_up(); + + timeleft = wait_event_interruptible_timeout(req->rq_wait, + req->rq_flags & SMB_REQ_RECEIVED, 30*HZ); + if (!timeleft || signal_pending(current)) { + /* + * On timeout or on interrupt we want to try and remove the + * request from the recvq/xmitq. + * First check if the request is still part of a queue. (May + * have been removed by some error condition) + */ + smb_lock_server(server); + if (!list_empty(&req->rq_queue)) { + list_del_init(&req->rq_queue); + smb_rput(req); + } + smb_unlock_server(server); + } + + if (!timeleft) { + PARANOIA("request [%p, mid=%d] timed out!\n", + req, req->rq_mid); + VERBOSE("smb_com: %02x\n", *(req->rq_header + smb_com)); + VERBOSE("smb_rcls: %02x\n", *(req->rq_header + smb_rcls)); + VERBOSE("smb_flg: %02x\n", *(req->rq_header + smb_flg)); + VERBOSE("smb_tid: %04x\n", WVAL(req->rq_header, smb_tid)); + VERBOSE("smb_pid: %04x\n", WVAL(req->rq_header, smb_pid)); + VERBOSE("smb_uid: %04x\n", WVAL(req->rq_header, smb_uid)); + VERBOSE("smb_mid: %04x\n", WVAL(req->rq_header, smb_mid)); + VERBOSE("smb_wct: %02x\n", *(req->rq_header + smb_wct)); + + req->rq_rcls = ERRSRV; + req->rq_err = ERRtimeout; + + /* Just in case it was "stuck" */ + smbiod_wake_up(); + } + VERBOSE("woke up, rcls=%d\n", req->rq_rcls); + + if (req->rq_rcls != 0) + req->rq_errno = smb_errno(req); + if (signal_pending(current)) + req->rq_errno = -ERESTARTSYS; + return req->rq_errno; +} + +/* + * Send a request and place it on the recvq if successfully sent. + * Must be called with the server lock held. + */ +static int smb_request_send_req(struct smb_request *req) +{ + struct smb_sb_info *server = req->rq_server; + int result; + + if (req->rq_bytes_sent == 0) { + WSET(req->rq_header, smb_tid, server->opt.tid); + WSET(req->rq_header, smb_pid, 1); + WSET(req->rq_header, smb_uid, server->opt.server_uid); + } + + result = smb_send_request(req); + if (result < 0 && result != -EAGAIN) + goto out; + + result = 0; + if (!(req->rq_flags & SMB_REQ_TRANSMITTED)) + goto out; + + list_move_tail(&req->rq_queue, &server->recvq); + result = 1; +out: + return result; +} + +/* + * Sends one request for this server. (smbiod) + * Must be called with the server lock held. + * Returns: <0 on error + * 0 if no request could be completely sent + * 1 if all data for one request was sent + */ +int smb_request_send_server(struct smb_sb_info *server) +{ + struct list_head *head; + struct smb_request *req; + int result; + + if (server->state != CONN_VALID) + return 0; + + /* dequeue first request, if any */ + req = NULL; + head = server->xmitq.next; + if (head != &server->xmitq) { + req = list_entry(head, struct smb_request, rq_queue); + } + if (!req) + return 0; + + result = smb_request_send_req(req); + if (result < 0) { + server->conn_error = result; + list_move(&req->rq_queue, &server->xmitq); + result = -EIO; + goto out; + } + +out: + return result; +} + +/* + * Try to find a request matching this "mid". Typically the first entry will + * be the matching one. + */ +static struct smb_request *find_request(struct smb_sb_info *server, int mid) +{ + struct list_head *tmp; + struct smb_request *req = NULL; + + list_for_each(tmp, &server->recvq) { + req = list_entry(tmp, struct smb_request, rq_queue); + if (req->rq_mid == mid) { + break; + } + req = NULL; + } + + if (!req) { + VERBOSE("received reply with mid %d but no request!\n", + WVAL(server->header, smb_mid)); + server->rstate = SMB_RECV_DROP; + } + + return req; +} + +/* + * Called when we have read the smb header and believe this is a response. + */ +static int smb_init_request(struct smb_sb_info *server, struct smb_request *req) +{ + int hdrlen, wct; + + memcpy(req->rq_header, server->header, SMB_HEADER_LEN); + + wct = *(req->rq_header + smb_wct); + if (wct > 20) { + PARANOIA("wct too large, %d > 20\n", wct); + server->rstate = SMB_RECV_DROP; + return 0; + } + + req->rq_resp_wct = wct; + hdrlen = SMB_HEADER_LEN + wct*2 + 2; + VERBOSE("header length: %d smb_wct: %2d\n", hdrlen, wct); + + req->rq_bytes_recvd = SMB_HEADER_LEN; + req->rq_rlen = hdrlen; + req->rq_iov[0].iov_base = req->rq_header; + req->rq_iov[0].iov_len = hdrlen; + req->rq_iovlen = 1; + server->rstate = SMB_RECV_PARAM; + +#ifdef SMB_DEBUG_PACKET_SIZE + add_recv_stats(smb_len(server->header)); +#endif + return 0; +} + +/* + * Reads the SMB parameters + */ +static int smb_recv_param(struct smb_sb_info *server, struct smb_request *req) +{ + int result; + + result = smb_receive(server, req); + if (result < 0) + return result; + if (req->rq_bytes_recvd < req->rq_rlen) + return 0; + + VERBOSE("result: %d smb_bcc: %04x\n", result, + WVAL(req->rq_header, SMB_HEADER_LEN + + (*(req->rq_header + smb_wct) * 2))); + + result = 0; + req->rq_iov[0].iov_base = NULL; + req->rq_rlen = 0; + if (req->rq_callback) + req->rq_callback(req); + else if (req->rq_setup_read) + result = req->rq_setup_read(req); + if (result < 0) { + server->rstate = SMB_RECV_DROP; + return result; + } + + server->rstate = req->rq_rlen > 0 ? SMB_RECV_DATA : SMB_RECV_END; + + req->rq_bytes_recvd = 0; // recvd out of the iov + + VERBOSE("rlen: %d\n", req->rq_rlen); + if (req->rq_rlen < 0) { + PARANOIA("Parameters read beyond end of packet!\n"); + server->rstate = SMB_RECV_END; + return -EIO; + } + return 0; +} + +/* + * Reads the SMB data + */ +static int smb_recv_data(struct smb_sb_info *server, struct smb_request *req) +{ + int result; + + result = smb_receive(server, req); + if (result < 0) + goto out; + if (req->rq_bytes_recvd < req->rq_rlen) + goto out; + server->rstate = SMB_RECV_END; +out: + VERBOSE("result: %d\n", result); + return result; +} + +/* + * Receive a transaction2 response + * Return: 0 if the response has been fully read + * 1 if there are further "fragments" to read + * <0 if there is an error + */ +static int smb_recv_trans2(struct smb_sb_info *server, struct smb_request *req) +{ + unsigned char *inbuf; + unsigned int parm_disp, parm_offset, parm_count, parm_tot; + unsigned int data_disp, data_offset, data_count, data_tot; + int hdrlen = SMB_HEADER_LEN + req->rq_resp_wct*2 - 2; + + VERBOSE("handling trans2\n"); + + inbuf = req->rq_header; + data_tot = WVAL(inbuf, smb_tdrcnt); + parm_tot = WVAL(inbuf, smb_tprcnt); + parm_disp = WVAL(inbuf, smb_prdisp); + parm_offset = WVAL(inbuf, smb_proff); + parm_count = WVAL(inbuf, smb_prcnt); + data_disp = WVAL(inbuf, smb_drdisp); + data_offset = WVAL(inbuf, smb_droff); + data_count = WVAL(inbuf, smb_drcnt); + + /* Modify offset for the split header/buffer we use */ + if (data_count || data_offset) { + if (unlikely(data_offset < hdrlen)) + goto out_bad_data; + else + data_offset -= hdrlen; + } + if (parm_count || parm_offset) { + if (unlikely(parm_offset < hdrlen)) + goto out_bad_parm; + else + parm_offset -= hdrlen; + } + + if (parm_count == parm_tot && data_count == data_tot) { + /* + * This packet has all the trans2 data. + * + * We setup the request so that this will be the common + * case. It may be a server error to not return a + * response that fits. + */ + VERBOSE("single trans2 response " + "dcnt=%u, pcnt=%u, doff=%u, poff=%u\n", + data_count, parm_count, + data_offset, parm_offset); + req->rq_ldata = data_count; + req->rq_lparm = parm_count; + req->rq_data = req->rq_buffer + data_offset; + req->rq_parm = req->rq_buffer + parm_offset; + if (unlikely(parm_offset + parm_count > req->rq_rlen)) + goto out_bad_parm; + if (unlikely(data_offset + data_count > req->rq_rlen)) + goto out_bad_data; + return 0; + } + + VERBOSE("multi trans2 response " + "frag=%d, dcnt=%u, pcnt=%u, doff=%u, poff=%u\n", + req->rq_fragment, + data_count, parm_count, + data_offset, parm_offset); + + if (!req->rq_fragment) { + int buf_len; + + /* We got the first trans2 fragment */ + req->rq_fragment = 1; + req->rq_total_data = data_tot; + req->rq_total_parm = parm_tot; + req->rq_ldata = 0; + req->rq_lparm = 0; + + buf_len = data_tot + parm_tot; + if (buf_len > SMB_MAX_PACKET_SIZE) + goto out_too_long; + + req->rq_trans2bufsize = buf_len; + req->rq_trans2buffer = kzalloc(buf_len, GFP_NOFS); + if (!req->rq_trans2buffer) + goto out_no_mem; + + req->rq_parm = req->rq_trans2buffer; + req->rq_data = req->rq_trans2buffer + parm_tot; + } else if (unlikely(req->rq_total_data < data_tot || + req->rq_total_parm < parm_tot)) + goto out_data_grew; + + if (unlikely(parm_disp + parm_count > req->rq_total_parm || + parm_offset + parm_count > req->rq_rlen)) + goto out_bad_parm; + if (unlikely(data_disp + data_count > req->rq_total_data || + data_offset + data_count > req->rq_rlen)) + goto out_bad_data; + + inbuf = req->rq_buffer; + memcpy(req->rq_parm + parm_disp, inbuf + parm_offset, parm_count); + memcpy(req->rq_data + data_disp, inbuf + data_offset, data_count); + + req->rq_ldata += data_count; + req->rq_lparm += parm_count; + + /* + * Check whether we've received all of the data. Note that + * we use the packet totals -- total lengths might shrink! + */ + if (req->rq_ldata >= data_tot && req->rq_lparm >= parm_tot) { + req->rq_ldata = data_tot; + req->rq_lparm = parm_tot; + return 0; + } + return 1; + +out_too_long: + printk(KERN_ERR "smb_trans2: data/param too long, data=%u, parm=%u\n", + data_tot, parm_tot); + goto out_EIO; +out_no_mem: + printk(KERN_ERR "smb_trans2: couldn't allocate data area of %d bytes\n", + req->rq_trans2bufsize); + req->rq_errno = -ENOMEM; + goto out; +out_data_grew: + printk(KERN_ERR "smb_trans2: data/params grew!\n"); + goto out_EIO; +out_bad_parm: + printk(KERN_ERR "smb_trans2: invalid parms, disp=%u, cnt=%u, tot=%u, ofs=%u\n", + parm_disp, parm_count, parm_tot, parm_offset); + goto out_EIO; +out_bad_data: + printk(KERN_ERR "smb_trans2: invalid data, disp=%u, cnt=%u, tot=%u, ofs=%u\n", + data_disp, data_count, data_tot, data_offset); +out_EIO: + req->rq_errno = -EIO; +out: + return req->rq_errno; +} + +/* + * State machine for receiving responses. We handle the fact that we can't + * read the full response in one try by having states telling us how much we + * have read. + * + * Must be called with the server lock held (only called from smbiod). + * + * Return: <0 on error + */ +int smb_request_recv(struct smb_sb_info *server) +{ + struct smb_request *req = NULL; + int result = 0; + + if (smb_recv_available(server) <= 0) + return 0; + + VERBOSE("state: %d\n", server->rstate); + switch (server->rstate) { + case SMB_RECV_DROP: + result = smb_receive_drop(server); + if (result < 0) + break; + if (server->rstate == SMB_RECV_DROP) + break; + server->rstate = SMB_RECV_START; + /* fallthrough */ + case SMB_RECV_START: + server->smb_read = 0; + server->rstate = SMB_RECV_HEADER; + /* fallthrough */ + case SMB_RECV_HEADER: + result = smb_receive_header(server); + if (result < 0) + break; + if (server->rstate == SMB_RECV_HEADER) + break; + if (! (*(server->header + smb_flg) & SMB_FLAGS_REPLY) ) { + server->rstate = SMB_RECV_REQUEST; + break; + } + if (server->rstate != SMB_RECV_HCOMPLETE) + break; + /* fallthrough */ + case SMB_RECV_HCOMPLETE: + req = find_request(server, WVAL(server->header, smb_mid)); + if (!req) + break; + smb_init_request(server, req); + req->rq_rcls = *(req->rq_header + smb_rcls); + req->rq_err = WVAL(req->rq_header, smb_err); + if (server->rstate != SMB_RECV_PARAM) + break; + /* fallthrough */ + case SMB_RECV_PARAM: + if (!req) + req = find_request(server,WVAL(server->header,smb_mid)); + if (!req) + break; + result = smb_recv_param(server, req); + if (result < 0) + break; + if (server->rstate != SMB_RECV_DATA) + break; + /* fallthrough */ + case SMB_RECV_DATA: + if (!req) + req = find_request(server,WVAL(server->header,smb_mid)); + if (!req) + break; + result = smb_recv_data(server, req); + if (result < 0) + break; + break; + + /* We should never be called with any of these states */ + case SMB_RECV_END: + case SMB_RECV_REQUEST: + BUG(); + } + + if (result < 0) { + /* We saw an error */ + return result; + } + + if (server->rstate != SMB_RECV_END) + return 0; + + result = 0; + if (req->rq_trans2_command && req->rq_rcls == SUCCESS) + result = smb_recv_trans2(server, req); + + /* + * Response completely read. Drop any extra bytes sent by the server. + * (Yes, servers sometimes add extra bytes to responses) + */ + VERBOSE("smb_len: %d smb_read: %d\n", + server->smb_len, server->smb_read); + if (server->smb_read < server->smb_len) + smb_receive_drop(server); + + server->rstate = SMB_RECV_START; + + if (!result) { + list_del_init(&req->rq_queue); + req->rq_flags |= SMB_REQ_RECEIVED; + smb_rput(req); + wake_up_interruptible(&req->rq_wait); + } + return 0; +} diff --git a/drivers/staging/smbfs/request.h b/drivers/staging/smbfs/request.h new file mode 100644 index 000000000000..efb21451e7c9 --- /dev/null +++ b/drivers/staging/smbfs/request.h @@ -0,0 +1,70 @@ +#include +#include +#include +#include + +struct smb_request { + struct list_head rq_queue; /* recvq or xmitq for the server */ + + atomic_t rq_count; + + wait_queue_head_t rq_wait; + int rq_flags; + int rq_mid; /* multiplex ID, set by request.c */ + + struct smb_sb_info *rq_server; + + /* header + word count + parameter words + byte count */ + unsigned char rq_header[SMB_HEADER_LEN + 20*2 + 2]; + + int rq_bufsize; + unsigned char *rq_buffer; + + /* FIXME: this is not good enough for merging IO requests. */ + unsigned char *rq_page; + int rq_rsize; + + int rq_resp_wct; + int rq_resp_bcc; + + int rq_rlen; + int rq_bytes_recvd; + + int rq_slen; + int rq_bytes_sent; + + int rq_iovlen; + struct kvec rq_iov[4]; + + int (*rq_setup_read) (struct smb_request *); + void (*rq_callback) (struct smb_request *); + + /* ------ trans2 stuff ------ */ + + u16 rq_trans2_command; /* 0 if not a trans2 request */ + unsigned int rq_ldata; + unsigned char *rq_data; + unsigned int rq_lparm; + unsigned char *rq_parm; + + int rq_fragment; + u32 rq_total_data; + u32 rq_total_parm; + int rq_trans2bufsize; + unsigned char *rq_trans2buffer; + + /* ------ response ------ */ + + unsigned short rq_rcls; + unsigned short rq_err; + int rq_errno; +}; + +#define SMB_REQ_STATIC 0x0001 /* rq_buffer is static */ +#define SMB_REQ_NORETRY 0x0002 /* request is invalid after retry */ + +#define SMB_REQ_TRANSMITTED 0x4000 /* all data has been sent */ +#define SMB_REQ_RECEIVED 0x8000 /* reply received, smbiod is done */ + +#define xSMB_REQ_NOREPLY 0x0004 /* we don't want the reply (if any) */ +#define xSMB_REQ_NORECEIVER 0x0008 /* caller doesn't wait for response */ diff --git a/drivers/staging/smbfs/smb.h b/drivers/staging/smbfs/smb.h new file mode 100644 index 000000000000..82fefddc5987 --- /dev/null +++ b/drivers/staging/smbfs/smb.h @@ -0,0 +1,118 @@ +/* + * smb.h + * + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + */ + +#ifndef _LINUX_SMB_H +#define _LINUX_SMB_H + +#include +#include +#ifdef __KERNEL__ +#include +#endif + +enum smb_protocol { + SMB_PROTOCOL_NONE, + SMB_PROTOCOL_CORE, + SMB_PROTOCOL_COREPLUS, + SMB_PROTOCOL_LANMAN1, + SMB_PROTOCOL_LANMAN2, + SMB_PROTOCOL_NT1 +}; + +enum smb_case_hndl { + SMB_CASE_DEFAULT, + SMB_CASE_LOWER, + SMB_CASE_UPPER +}; + +struct smb_dskattr { + __u16 total; + __u16 allocblocks; + __u16 blocksize; + __u16 free; +}; + +struct smb_conn_opt { + + /* The socket */ + unsigned int fd; + + enum smb_protocol protocol; + enum smb_case_hndl case_handling; + + /* Connection-Options */ + + __u32 max_xmit; + __u16 server_uid; + __u16 tid; + + /* The following are LANMAN 1.0 options */ + __u16 secmode; + __u16 maxmux; + __u16 maxvcs; + __u16 rawmode; + __u32 sesskey; + + /* The following are NT LM 0.12 options */ + __u32 maxraw; + __u32 capabilities; + __s16 serverzone; +}; + +#ifdef __KERNEL__ + +#define SMB_NLS_MAXNAMELEN 20 +struct smb_nls_codepage { + char local_name[SMB_NLS_MAXNAMELEN]; + char remote_name[SMB_NLS_MAXNAMELEN]; +}; + + +#define SMB_MAXNAMELEN 255 +#define SMB_MAXPATHLEN 1024 + +/* + * Contains all relevant data on a SMB networked file. + */ +struct smb_fattr { + __u16 attr; + + unsigned long f_ino; + umode_t f_mode; + nlink_t f_nlink; + uid_t f_uid; + gid_t f_gid; + dev_t f_rdev; + loff_t f_size; + struct timespec f_atime; + struct timespec f_mtime; + struct timespec f_ctime; + unsigned long f_blocks; + int f_unix; +}; + +enum smb_conn_state { + CONN_VALID, /* everything's fine */ + CONN_INVALID, /* Something went wrong, but did not + try to reconnect yet. */ + CONN_RETRIED, /* Tried a reconnection, but was refused */ + CONN_RETRYING /* Currently trying to reconnect */ +}; + +#define SMB_HEADER_LEN 37 /* includes everything up to, but not + * including smb_bcc */ + +#define SMB_INITIAL_PACKET_SIZE 4000 +#define SMB_MAX_PACKET_SIZE 32768 + +/* reserve this much space for trans2 parameters. Shouldn't have to be more + than 10 or so, but OS/2 seems happier like this. */ +#define SMB_TRANS2_MAX_PARAM 64 + +#endif +#endif diff --git a/drivers/staging/smbfs/smb_debug.h b/drivers/staging/smbfs/smb_debug.h new file mode 100644 index 000000000000..fc4b1a5dd755 --- /dev/null +++ b/drivers/staging/smbfs/smb_debug.h @@ -0,0 +1,34 @@ +/* + * Defines some debug macros for smbfs. + */ + +/* This makes a dentry parent/child name pair. Useful for debugging printk's */ +#define DENTRY_PATH(dentry) \ + (dentry)->d_parent->d_name.name,(dentry)->d_name.name + +/* + * safety checks that should never happen ??? + * these are normally enabled. + */ +#ifdef SMBFS_PARANOIA +# define PARANOIA(f, a...) printk(KERN_NOTICE "%s: " f, __func__ , ## a) +#else +# define PARANOIA(f, a...) do { ; } while(0) +#endif + +/* lots of debug messages */ +#ifdef SMBFS_DEBUG_VERBOSE +# define VERBOSE(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a) +#else +# define VERBOSE(f, a...) do { ; } while(0) +#endif + +/* + * "normal" debug messages, but not with a normal DEBUG define ... way + * too common name. + */ +#ifdef SMBFS_DEBUG +#define DEBUG1(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a) +#else +#define DEBUG1(f, a...) do { ; } while(0) +#endif diff --git a/drivers/staging/smbfs/smb_fs.h b/drivers/staging/smbfs/smb_fs.h new file mode 100644 index 000000000000..20a05c188eb9 --- /dev/null +++ b/drivers/staging/smbfs/smb_fs.h @@ -0,0 +1,153 @@ +/* + * smb_fs.h + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + */ + +#ifndef _LINUX_SMB_FS_H +#define _LINUX_SMB_FS_H + +#include "smb.h" + +/* + * ioctl commands + */ +#define SMB_IOC_GETMOUNTUID _IOR('u', 1, __kernel_old_uid_t) +#define SMB_IOC_NEWCONN _IOW('u', 2, struct smb_conn_opt) + +/* __kernel_uid_t can never change, so we have to use __kernel_uid32_t */ +#define SMB_IOC_GETMOUNTUID32 _IOR('u', 3, __kernel_uid32_t) + + +#ifdef __KERNEL__ +#include "smb_fs_i.h" +#include "smb_fs_sb.h" +#include "smb_mount.h" + +#include +#include +#include +#include +#include + +static inline struct smb_sb_info *SMB_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +static inline struct smb_inode_info *SMB_I(struct inode *inode) +{ + return container_of(inode, struct smb_inode_info, vfs_inode); +} + +/* macro names are short for word, double-word, long value (?) */ +#define WVAL(buf, pos) (get_unaligned_le16((u8 *)(buf) + (pos))) +#define DVAL(buf, pos) (get_unaligned_le32((u8 *)(buf) + (pos))) +#define LVAL(buf, pos) (get_unaligned_le64((u8 *)(buf) + (pos))) + +#define WSET(buf, pos, val) put_unaligned_le16((val), (u8 *)(buf) + (pos)) +#define DSET(buf, pos, val) put_unaligned_le32((val), (u8 *)(buf) + (pos)) +#define LSET(buf, pos, val) put_unaligned_le64((val), (u8 *)(buf) + (pos)) + +/* where to find the base of the SMB packet proper */ +#define smb_base(buf) ((u8 *)(((u8 *)(buf))+4)) + +/* + * Flags for the in-memory inode + */ +#define SMB_F_LOCALWRITE 0x02 /* file modified locally */ + + +/* NT1 protocol capability bits */ +#define SMB_CAP_RAW_MODE 0x00000001 +#define SMB_CAP_MPX_MODE 0x00000002 +#define SMB_CAP_UNICODE 0x00000004 +#define SMB_CAP_LARGE_FILES 0x00000008 +#define SMB_CAP_NT_SMBS 0x00000010 +#define SMB_CAP_RPC_REMOTE_APIS 0x00000020 +#define SMB_CAP_STATUS32 0x00000040 +#define SMB_CAP_LEVEL_II_OPLOCKS 0x00000080 +#define SMB_CAP_LOCK_AND_READ 0x00000100 +#define SMB_CAP_NT_FIND 0x00000200 +#define SMB_CAP_DFS 0x00001000 +#define SMB_CAP_LARGE_READX 0x00004000 +#define SMB_CAP_LARGE_WRITEX 0x00008000 +#define SMB_CAP_UNIX 0x00800000 /* unofficial ... */ + + +/* + * This is the time we allow an inode, dentry or dir cache to live. It is bad + * for performance to have shorter ttl on an inode than on the cache. It can + * cause refresh on each inode for a dir listing ... one-by-one + */ +#define SMB_MAX_AGE(server) (((server)->mnt->ttl * HZ) / 1000) + +static inline void +smb_age_dentry(struct smb_sb_info *server, struct dentry *dentry) +{ + dentry->d_time = jiffies - SMB_MAX_AGE(server); +} + +struct smb_cache_head { + time_t mtime; /* unused */ + unsigned long time; /* cache age */ + unsigned long end; /* last valid fpos in cache */ + int eof; +}; + +#define SMB_DIRCACHE_SIZE ((int)(PAGE_CACHE_SIZE/sizeof(struct dentry *))) +union smb_dir_cache { + struct smb_cache_head head; + struct dentry *dentry[SMB_DIRCACHE_SIZE]; +}; + +#define SMB_FIRSTCACHE_SIZE ((int)((SMB_DIRCACHE_SIZE * \ + sizeof(struct dentry *) - sizeof(struct smb_cache_head)) / \ + sizeof(struct dentry *))) + +#define SMB_DIRCACHE_START (SMB_DIRCACHE_SIZE - SMB_FIRSTCACHE_SIZE) + +struct smb_cache_control { + struct smb_cache_head head; + struct page *page; + union smb_dir_cache *cache; + unsigned long fpos, ofs; + int filled, valid, idx; +}; + +#define SMB_OPS_NUM_STATIC 5 +struct smb_ops { + int (*read)(struct inode *inode, loff_t offset, int count, + char *data); + int (*write)(struct inode *inode, loff_t offset, int count, const + char *data); + int (*readdir)(struct file *filp, void *dirent, filldir_t filldir, + struct smb_cache_control *ctl); + + int (*getattr)(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *fattr); + /* int (*setattr)(...); */ /* setattr is really icky! */ + + int (*truncate)(struct inode *inode, loff_t length); + + + /* --- --- --- end of "static" entries --- --- --- */ + + int (*convert)(unsigned char *output, int olen, + const unsigned char *input, int ilen, + struct nls_table *nls_from, + struct nls_table *nls_to); +}; + +static inline int +smb_is_open(struct inode *i) +{ + return (SMB_I(i)->open == server_from_inode(i)->generation); +} + +extern void smb_install_null_ops(struct smb_ops *); +#endif /* __KERNEL__ */ + +#endif /* _LINUX_SMB_FS_H */ diff --git a/drivers/staging/smbfs/smb_fs_i.h b/drivers/staging/smbfs/smb_fs_i.h new file mode 100644 index 000000000000..8ccf4eca2c3d --- /dev/null +++ b/drivers/staging/smbfs/smb_fs_i.h @@ -0,0 +1,37 @@ +/* + * smb_fs_i.h + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + */ + +#ifndef _LINUX_SMB_FS_I +#define _LINUX_SMB_FS_I + +#include +#include + +/* + * smb fs inode data (in memory only) + */ +struct smb_inode_info { + + /* + * file handles are local to a connection. A file is open if + * (open == generation). + */ + unsigned int open; /* open generation */ + __u16 fileid; /* What id to handle a file with? */ + __u16 attr; /* Attribute fields, DOS value */ + + __u16 access; /* Access mode */ + __u16 flags; + unsigned long oldmtime; /* last time refreshed */ + unsigned long closed; /* timestamp when closed */ + unsigned openers; /* number of fileid users */ + + struct inode vfs_inode; /* must be at the end */ +}; + +#endif diff --git a/drivers/staging/smbfs/smb_fs_sb.h b/drivers/staging/smbfs/smb_fs_sb.h new file mode 100644 index 000000000000..ca058afda900 --- /dev/null +++ b/drivers/staging/smbfs/smb_fs_sb.h @@ -0,0 +1,100 @@ +/* + * smb_fs_sb.h + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + */ + +#ifndef _SMB_FS_SB +#define _SMB_FS_SB + +#include +#include +#include "smb.h" + +/* + * Upper limit on the total number of active smb_request structs. + */ +#define MAX_REQUEST_HARD 256 + +enum smb_receive_state { + SMB_RECV_START, /* No data read, looking for length + sig */ + SMB_RECV_HEADER, /* Reading the header data */ + SMB_RECV_HCOMPLETE, /* Done with the header */ + SMB_RECV_PARAM, /* Reading parameter words */ + SMB_RECV_DATA, /* Reading data bytes */ + SMB_RECV_END, /* End of request */ + SMB_RECV_DROP, /* Dropping this SMB */ + SMB_RECV_REQUEST, /* Received a request and not a reply */ +}; + +/* structure access macros */ +#define server_from_inode(inode) SMB_SB((inode)->i_sb) +#define server_from_dentry(dentry) SMB_SB((dentry)->d_sb) +#define SB_of(server) ((server)->super_block) + +struct smb_sb_info { + /* List of all smbfs superblocks */ + struct list_head entry; + + enum smb_conn_state state; + struct file * sock_file; + int conn_error; + enum smb_receive_state rstate; + + atomic_t nr_requests; + struct list_head xmitq; + struct list_head recvq; + u16 mid; + + struct smb_mount_data_kernel *mnt; + + /* Connections are counted. Each time a new socket arrives, + * generation is incremented. + */ + unsigned int generation; + struct pid *conn_pid; + struct smb_conn_opt opt; + wait_queue_head_t conn_wq; + int conn_complete; + struct semaphore sem; + + unsigned char header[SMB_HEADER_LEN + 20*2 + 2]; + u32 header_len; + u32 smb_len; + u32 smb_read; + + /* We use our own data_ready callback, but need the original one */ + void *data_ready; + + /* nls pointers for codepage conversions */ + struct nls_table *remote_nls; + struct nls_table *local_nls; + + struct smb_ops *ops; + + struct super_block *super_block; + + struct backing_dev_info bdi; +}; + +static inline int +smb_lock_server_interruptible(struct smb_sb_info *server) +{ + return down_interruptible(&(server->sem)); +} + +static inline void +smb_lock_server(struct smb_sb_info *server) +{ + down(&(server->sem)); +} + +static inline void +smb_unlock_server(struct smb_sb_info *server) +{ + up(&(server->sem)); +} + +#endif diff --git a/drivers/staging/smbfs/smb_mount.h b/drivers/staging/smbfs/smb_mount.h new file mode 100644 index 000000000000..d10f00cb5703 --- /dev/null +++ b/drivers/staging/smbfs/smb_mount.h @@ -0,0 +1,65 @@ +/* + * smb_mount.h + * + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + */ + +#ifndef _LINUX_SMB_MOUNT_H +#define _LINUX_SMB_MOUNT_H + +#include + +#define SMB_MOUNT_VERSION 6 + +struct smb_mount_data { + int version; + __kernel_uid_t mounted_uid; /* Who may umount() this filesystem? */ + __kernel_uid_t uid; + __kernel_gid_t gid; + __kernel_mode_t file_mode; + __kernel_mode_t dir_mode; +}; + + +#ifdef __KERNEL__ + +/* "vers" in big-endian */ +#define SMB_MOUNT_ASCII 0x76657273 + +#define SMB_MOUNT_OLDVERSION 6 +#undef SMB_MOUNT_VERSION +#define SMB_MOUNT_VERSION 7 + +/* flags */ +#define SMB_MOUNT_WIN95 0x0001 /* Win 95 server */ +#define SMB_MOUNT_OLDATTR 0x0002 /* Use core getattr (Win 95 speedup) */ +#define SMB_MOUNT_DIRATTR 0x0004 /* Use find_first for getattr */ +#define SMB_MOUNT_CASE 0x0008 /* Be case sensitive */ +#define SMB_MOUNT_UNICODE 0x0010 /* Server talks unicode */ +#define SMB_MOUNT_UID 0x0020 /* Use user specified uid */ +#define SMB_MOUNT_GID 0x0040 /* Use user specified gid */ +#define SMB_MOUNT_FMODE 0x0080 /* Use user specified file mode */ +#define SMB_MOUNT_DMODE 0x0100 /* Use user specified dir mode */ + +struct smb_mount_data_kernel { + int version; + + uid_t mounted_uid; /* Who may umount() this filesystem? */ + uid_t uid; + gid_t gid; + mode_t file_mode; + mode_t dir_mode; + + u32 flags; + + /* maximum age in jiffies (inode, dentry and dircache) */ + int ttl; + + struct smb_nls_codepage codepage; +}; + +#endif + +#endif diff --git a/drivers/staging/smbfs/smbfs.txt b/drivers/staging/smbfs/smbfs.txt new file mode 100644 index 000000000000..194fb0decd2c --- /dev/null +++ b/drivers/staging/smbfs/smbfs.txt @@ -0,0 +1,8 @@ +Smbfs is a filesystem that implements the SMB protocol, which is the +protocol used by Windows for Workgroups, Windows 95 and Windows NT. +Smbfs was inspired by Samba, the program written by Andrew Tridgell +that turns any Unix host into a file server for DOS or Windows clients. + +Smbfs is a SMB client, but uses parts of samba for its operation. For +more info on samba, including documentation, please go to +http://www.samba.org/ and then on to your nearest mirror. diff --git a/drivers/staging/smbfs/smbiod.c b/drivers/staging/smbfs/smbiod.c new file mode 100644 index 000000000000..ec998920f8d9 --- /dev/null +++ b/drivers/staging/smbfs/smbiod.c @@ -0,0 +1,343 @@ +/* + * smbiod.c + * + * Copyright (C) 2000, Charles Loep / Corel Corp. + * Copyright (C) 2001, Urban Widmark + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "smb_fs.h" +#include "smbno.h" +#include "smb_mount.h" +#include "smb_debug.h" +#include "request.h" +#include "proto.h" + +enum smbiod_state { + SMBIOD_DEAD, + SMBIOD_STARTING, + SMBIOD_RUNNING, +}; + +static enum smbiod_state smbiod_state = SMBIOD_DEAD; +static struct task_struct *smbiod_thread; +static DECLARE_WAIT_QUEUE_HEAD(smbiod_wait); +static LIST_HEAD(smb_servers); +static DEFINE_SPINLOCK(servers_lock); + +#define SMBIOD_DATA_READY (1<<0) +static unsigned long smbiod_flags; + +static int smbiod(void *); +static int smbiod_start(void); + +/* + * called when there's work for us to do + */ +void smbiod_wake_up(void) +{ + if (smbiod_state == SMBIOD_DEAD) + return; + set_bit(SMBIOD_DATA_READY, &smbiod_flags); + wake_up_interruptible(&smbiod_wait); +} + +/* + * start smbiod if none is running + */ +static int smbiod_start(void) +{ + struct task_struct *tsk; + int err = 0; + + if (smbiod_state != SMBIOD_DEAD) + return 0; + smbiod_state = SMBIOD_STARTING; + __module_get(THIS_MODULE); + spin_unlock(&servers_lock); + tsk = kthread_run(smbiod, NULL, "smbiod"); + if (IS_ERR(tsk)) { + err = PTR_ERR(tsk); + module_put(THIS_MODULE); + } + + spin_lock(&servers_lock); + if (err < 0) { + smbiod_state = SMBIOD_DEAD; + smbiod_thread = NULL; + } else { + smbiod_state = SMBIOD_RUNNING; + smbiod_thread = tsk; + } + return err; +} + +/* + * register a server & start smbiod if necessary + */ +int smbiod_register_server(struct smb_sb_info *server) +{ + int ret; + spin_lock(&servers_lock); + list_add(&server->entry, &smb_servers); + VERBOSE("%p\n", server); + ret = smbiod_start(); + spin_unlock(&servers_lock); + return ret; +} + +/* + * Unregister a server + * Must be called with the server lock held. + */ +void smbiod_unregister_server(struct smb_sb_info *server) +{ + spin_lock(&servers_lock); + list_del_init(&server->entry); + VERBOSE("%p\n", server); + spin_unlock(&servers_lock); + + smbiod_wake_up(); + smbiod_flush(server); +} + +void smbiod_flush(struct smb_sb_info *server) +{ + struct list_head *tmp, *n; + struct smb_request *req; + + list_for_each_safe(tmp, n, &server->xmitq) { + req = list_entry(tmp, struct smb_request, rq_queue); + req->rq_errno = -EIO; + list_del_init(&req->rq_queue); + smb_rput(req); + wake_up_interruptible(&req->rq_wait); + } + list_for_each_safe(tmp, n, &server->recvq) { + req = list_entry(tmp, struct smb_request, rq_queue); + req->rq_errno = -EIO; + list_del_init(&req->rq_queue); + smb_rput(req); + wake_up_interruptible(&req->rq_wait); + } +} + +/* + * Wake up smbmount and make it reconnect to the server. + * This must be called with the server locked. + * + * FIXME: add smbconnect version to this + */ +int smbiod_retry(struct smb_sb_info *server) +{ + struct list_head *head; + struct smb_request *req; + struct pid *pid = get_pid(server->conn_pid); + int result = 0; + + VERBOSE("state: %d\n", server->state); + if (server->state == CONN_VALID || server->state == CONN_RETRYING) + goto out; + + smb_invalidate_inodes(server); + + /* + * Some requests are meaningless after a retry, so we abort them. + * One example are all requests using 'fileid' since the files are + * closed on retry. + */ + head = server->xmitq.next; + while (head != &server->xmitq) { + req = list_entry(head, struct smb_request, rq_queue); + head = head->next; + + req->rq_bytes_sent = 0; + if (req->rq_flags & SMB_REQ_NORETRY) { + VERBOSE("aborting request %p on xmitq\n", req); + req->rq_errno = -EIO; + list_del_init(&req->rq_queue); + smb_rput(req); + wake_up_interruptible(&req->rq_wait); + } + } + + /* + * FIXME: test the code for retrying request we already sent + */ + head = server->recvq.next; + while (head != &server->recvq) { + req = list_entry(head, struct smb_request, rq_queue); + head = head->next; +#if 0 + if (req->rq_flags & SMB_REQ_RETRY) { + /* must move the request to the xmitq */ + VERBOSE("retrying request %p on recvq\n", req); + list_move(&req->rq_queue, &server->xmitq); + continue; + } +#endif + + VERBOSE("aborting request %p on recvq\n", req); + /* req->rq_rcls = ???; */ /* FIXME: set smb error code too? */ + req->rq_errno = -EIO; + list_del_init(&req->rq_queue); + smb_rput(req); + wake_up_interruptible(&req->rq_wait); + } + + smb_close_socket(server); + + if (!pid) { + /* FIXME: this is fatal, umount? */ + printk(KERN_ERR "smb_retry: no connection process\n"); + server->state = CONN_RETRIED; + goto out; + } + + /* + * Change state so that only one retry per server will be started. + */ + server->state = CONN_RETRYING; + + /* + * Note: use the "priv" flag, as a user process may need to reconnect. + */ + result = kill_pid(pid, SIGUSR1, 1); + if (result) { + /* FIXME: this is most likely fatal, umount? */ + printk(KERN_ERR "smb_retry: signal failed [%d]\n", result); + goto out; + } + VERBOSE("signalled pid %d\n", pid_nr(pid)); + + /* FIXME: The retried requests should perhaps get a "time boost". */ + +out: + put_pid(pid); + return result; +} + +/* + * Currently handles lockingX packets. + */ +static void smbiod_handle_request(struct smb_sb_info *server) +{ + PARANOIA("smbiod got a request ... and we don't implement oplocks!\n"); + server->rstate = SMB_RECV_DROP; +} + +/* + * Do some IO for one server. + */ +static void smbiod_doio(struct smb_sb_info *server) +{ + int result; + int maxwork = 7; + + if (server->state != CONN_VALID) + goto out; + + do { + result = smb_request_recv(server); + if (result < 0) { + server->state = CONN_INVALID; + smbiod_retry(server); + goto out; /* reconnecting is slow */ + } else if (server->rstate == SMB_RECV_REQUEST) + smbiod_handle_request(server); + } while (result > 0 && maxwork-- > 0); + + /* + * If there is more to read then we want to be sure to wake up again. + */ + if (server->state != CONN_VALID) + goto out; + if (smb_recv_available(server) > 0) + set_bit(SMBIOD_DATA_READY, &smbiod_flags); + + do { + result = smb_request_send_server(server); + if (result < 0) { + server->state = CONN_INVALID; + smbiod_retry(server); + goto out; /* reconnecting is slow */ + } + } while (result > 0); + + /* + * If the last request was not sent out we want to wake up again. + */ + if (!list_empty(&server->xmitq)) + set_bit(SMBIOD_DATA_READY, &smbiod_flags); + +out: + return; +} + +/* + * smbiod kernel thread + */ +static int smbiod(void *unused) +{ + VERBOSE("SMB Kernel thread starting (%d) ...\n", current->pid); + + for (;;) { + struct smb_sb_info *server; + struct list_head *pos, *n; + + /* FIXME: Use poll? */ + wait_event_interruptible(smbiod_wait, + test_bit(SMBIOD_DATA_READY, &smbiod_flags)); + if (signal_pending(current)) { + spin_lock(&servers_lock); + smbiod_state = SMBIOD_DEAD; + spin_unlock(&servers_lock); + break; + } + + clear_bit(SMBIOD_DATA_READY, &smbiod_flags); + + spin_lock(&servers_lock); + if (list_empty(&smb_servers)) { + smbiod_state = SMBIOD_DEAD; + spin_unlock(&servers_lock); + break; + } + + list_for_each_safe(pos, n, &smb_servers) { + server = list_entry(pos, struct smb_sb_info, entry); + VERBOSE("checking server %p\n", server); + + if (server->state == CONN_VALID) { + spin_unlock(&servers_lock); + + smb_lock_server(server); + smbiod_doio(server); + smb_unlock_server(server); + + spin_lock(&servers_lock); + } + } + spin_unlock(&servers_lock); + } + + VERBOSE("SMB Kernel thread exiting (%d) ...\n", current->pid); + module_put_and_exit(0); +} diff --git a/drivers/staging/smbfs/smbno.h b/drivers/staging/smbfs/smbno.h new file mode 100644 index 000000000000..f99e02d9ffe2 --- /dev/null +++ b/drivers/staging/smbfs/smbno.h @@ -0,0 +1,363 @@ +#ifndef _SMBNO_H_ +#define _SMBNO_H_ + +/* these define the attribute byte as seen by DOS */ +#define aRONLY (1L<<0) +#define aHIDDEN (1L<<1) +#define aSYSTEM (1L<<2) +#define aVOLID (1L<<3) +#define aDIR (1L<<4) +#define aARCH (1L<<5) + +/* error classes */ +#define SUCCESS 0 /* The request was successful. */ +#define ERRDOS 0x01 /* Error is from the core DOS operating system set. */ +#define ERRSRV 0x02 /* Error is generated by the server network file manager.*/ +#define ERRHRD 0x03 /* Error is an hardware error. */ +#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ + +/* SMB X/Open error codes for the ERRdos error class */ + +#define ERRbadfunc 1 /* Invalid function (or system call) */ +#define ERRbadfile 2 /* File not found (pathname error) */ +#define ERRbadpath 3 /* Directory not found */ +#define ERRnofids 4 /* Too many open files */ +#define ERRnoaccess 5 /* Access denied */ +#define ERRbadfid 6 /* Invalid fid */ +#define ERRbadmcb 7 /* Memory control blocks destroyed */ +#define ERRnomem 8 /* Out of memory */ +#define ERRbadmem 9 /* Invalid memory block address */ +#define ERRbadenv 10 /* Invalid environment */ +#define ERRbadformat 11 /* Invalid format */ +#define ERRbadaccess 12 /* Invalid open mode */ +#define ERRbaddata 13 /* Invalid data (only from ioctl call) */ +#define ERRres 14 /* reserved */ +#define ERRbaddrive 15 /* Invalid drive */ +#define ERRremcd 16 /* Attempt to delete current directory */ +#define ERRdiffdevice 17 /* rename/move across different filesystems */ +#define ERRnofiles 18 /* no more files found in file search */ +#define ERRbadshare 32 /* Share mode on file conflict with open mode */ +#define ERRlock 33 /* Lock request conflicts with existing lock */ +#define ERRfilexists 80 /* File in operation already exists */ +#define ERRbadpipe 230 /* Named pipe invalid */ +#define ERRpipebusy 231 /* All instances of pipe are busy */ +#define ERRpipeclosing 232 /* named pipe close in progress */ +#define ERRnotconnected 233 /* No process on other end of named pipe */ +#define ERRmoredata 234 /* More data to be returned */ + +#define ERROR_INVALID_PARAMETER 87 +#define ERROR_DISK_FULL 112 +#define ERROR_INVALID_NAME 123 +#define ERROR_DIR_NOT_EMPTY 145 +#define ERROR_NOT_LOCKED 158 +#define ERROR_ALREADY_EXISTS 183 /* see also 80 ? */ +#define ERROR_EAS_DIDNT_FIT 275 /* Extended attributes didn't fit */ +#define ERROR_EAS_NOT_SUPPORTED 282 /* Extended attributes not supported */ + +/* Error codes for the ERRSRV class */ + +#define ERRerror 1 /* Non specific error code */ +#define ERRbadpw 2 /* Bad password */ +#define ERRbadtype 3 /* reserved */ +#define ERRaccess 4 /* No permissions to do the requested operation */ +#define ERRinvnid 5 /* tid invalid */ +#define ERRinvnetname 6 /* Invalid servername */ +#define ERRinvdevice 7 /* Invalid device */ +#define ERRqfull 49 /* Print queue full */ +#define ERRqtoobig 50 /* Queued item too big */ +#define ERRinvpfid 52 /* Invalid print file in smb_fid */ +#define ERRsmbcmd 64 /* Unrecognised command */ +#define ERRsrverror 65 /* smb server internal error */ +#define ERRfilespecs 67 /* fid and pathname invalid combination */ +#define ERRbadlink 68 /* reserved */ +#define ERRbadpermits 69 /* Access specified for a file is not valid */ +#define ERRbadpid 70 /* reserved */ +#define ERRsetattrmode 71 /* attribute mode invalid */ +#define ERRpaused 81 /* Message server paused */ +#define ERRmsgoff 82 /* Not receiving messages */ +#define ERRnoroom 83 /* No room for message */ +#define ERRrmuns 87 /* too many remote usernames */ +#define ERRtimeout 88 /* operation timed out */ +#define ERRnoresource 89 /* No resources currently available for request. */ +#define ERRtoomanyuids 90 /* too many userids */ +#define ERRbaduid 91 /* bad userid */ +#define ERRuseMPX 250 /* temporarily unable to use raw mode, use MPX mode */ +#define ERRuseSTD 251 /* temporarily unable to use raw mode, use std.mode */ +#define ERRcontMPX 252 /* resume MPX mode */ +#define ERRbadPW /* reserved */ +#define ERRnosupport 0xFFFF + +/* Error codes for the ERRHRD class */ + +#define ERRnowrite 19 /* read only media */ +#define ERRbadunit 20 /* Unknown device */ +#define ERRnotready 21 /* Drive not ready */ +#define ERRbadcmd 22 /* Unknown command */ +#define ERRdata 23 /* Data (CRC) error */ +#define ERRbadreq 24 /* Bad request structure length */ +#define ERRseek 25 +#define ERRbadmedia 26 +#define ERRbadsector 27 +#define ERRnopaper 28 +#define ERRwrite 29 /* write fault */ +#define ERRread 30 /* read fault */ +#define ERRgeneral 31 /* General hardware failure */ +#define ERRwrongdisk 34 +#define ERRFCBunavail 35 +#define ERRsharebufexc 36 /* share buffer exceeded */ +#define ERRdiskfull 39 + +/* + * Access modes when opening a file + */ +#define SMB_ACCMASK 0x0003 +#define SMB_O_RDONLY 0x0000 +#define SMB_O_WRONLY 0x0001 +#define SMB_O_RDWR 0x0002 + +/* offsets into message for common items */ +#define smb_com 8 +#define smb_rcls 9 +#define smb_reh 10 +#define smb_err 11 +#define smb_flg 13 +#define smb_flg2 14 +#define smb_reb 13 +#define smb_tid 28 +#define smb_pid 30 +#define smb_uid 32 +#define smb_mid 34 +#define smb_wct 36 +#define smb_vwv 37 +#define smb_vwv0 37 +#define smb_vwv1 39 +#define smb_vwv2 41 +#define smb_vwv3 43 +#define smb_vwv4 45 +#define smb_vwv5 47 +#define smb_vwv6 49 +#define smb_vwv7 51 +#define smb_vwv8 53 +#define smb_vwv9 55 +#define smb_vwv10 57 +#define smb_vwv11 59 +#define smb_vwv12 61 +#define smb_vwv13 63 +#define smb_vwv14 65 + +/* these are the trans2 sub fields for primary requests */ +#define smb_tpscnt smb_vwv0 +#define smb_tdscnt smb_vwv1 +#define smb_mprcnt smb_vwv2 +#define smb_mdrcnt smb_vwv3 +#define smb_msrcnt smb_vwv4 +#define smb_flags smb_vwv5 +#define smb_timeout smb_vwv6 +#define smb_pscnt smb_vwv9 +#define smb_psoff smb_vwv10 +#define smb_dscnt smb_vwv11 +#define smb_dsoff smb_vwv12 +#define smb_suwcnt smb_vwv13 +#define smb_setup smb_vwv14 +#define smb_setup0 smb_setup +#define smb_setup1 (smb_setup+2) +#define smb_setup2 (smb_setup+4) + +/* these are for the secondary requests */ +#define smb_spscnt smb_vwv2 +#define smb_spsoff smb_vwv3 +#define smb_spsdisp smb_vwv4 +#define smb_sdscnt smb_vwv5 +#define smb_sdsoff smb_vwv6 +#define smb_sdsdisp smb_vwv7 +#define smb_sfid smb_vwv8 + +/* and these for responses */ +#define smb_tprcnt smb_vwv0 +#define smb_tdrcnt smb_vwv1 +#define smb_prcnt smb_vwv3 +#define smb_proff smb_vwv4 +#define smb_prdisp smb_vwv5 +#define smb_drcnt smb_vwv6 +#define smb_droff smb_vwv7 +#define smb_drdisp smb_vwv8 + +/* the complete */ +#define SMBmkdir 0x00 /* create directory */ +#define SMBrmdir 0x01 /* delete directory */ +#define SMBopen 0x02 /* open file */ +#define SMBcreate 0x03 /* create file */ +#define SMBclose 0x04 /* close file */ +#define SMBflush 0x05 /* flush file */ +#define SMBunlink 0x06 /* delete file */ +#define SMBmv 0x07 /* rename file */ +#define SMBgetatr 0x08 /* get file attributes */ +#define SMBsetatr 0x09 /* set file attributes */ +#define SMBread 0x0A /* read from file */ +#define SMBwrite 0x0B /* write to file */ +#define SMBlock 0x0C /* lock byte range */ +#define SMBunlock 0x0D /* unlock byte range */ +#define SMBctemp 0x0E /* create temporary file */ +#define SMBmknew 0x0F /* make new file */ +#define SMBchkpth 0x10 /* check directory path */ +#define SMBexit 0x11 /* process exit */ +#define SMBlseek 0x12 /* seek */ +#define SMBtcon 0x70 /* tree connect */ +#define SMBtconX 0x75 /* tree connect and X*/ +#define SMBtdis 0x71 /* tree disconnect */ +#define SMBnegprot 0x72 /* negotiate protocol */ +#define SMBdskattr 0x80 /* get disk attributes */ +#define SMBsearch 0x81 /* search directory */ +#define SMBsplopen 0xC0 /* open print spool file */ +#define SMBsplwr 0xC1 /* write to print spool file */ +#define SMBsplclose 0xC2 /* close print spool file */ +#define SMBsplretq 0xC3 /* return print queue */ +#define SMBsends 0xD0 /* send single block message */ +#define SMBsendb 0xD1 /* send broadcast message */ +#define SMBfwdname 0xD2 /* forward user name */ +#define SMBcancelf 0xD3 /* cancel forward */ +#define SMBgetmac 0xD4 /* get machine name */ +#define SMBsendstrt 0xD5 /* send start of multi-block message */ +#define SMBsendend 0xD6 /* send end of multi-block message */ +#define SMBsendtxt 0xD7 /* send text of multi-block message */ + +/* Core+ protocol */ +#define SMBlockread 0x13 /* Lock a range and read */ +#define SMBwriteunlock 0x14 /* Unlock a range then write */ +#define SMBreadbraw 0x1a /* read a block of data with no smb header */ +#define SMBwritebraw 0x1d /* write a block of data with no smb header */ +#define SMBwritec 0x20 /* secondary write request */ +#define SMBwriteclose 0x2c /* write a file then close it */ + +/* dos extended protocol */ +#define SMBreadBraw 0x1A /* read block raw */ +#define SMBreadBmpx 0x1B /* read block multiplexed */ +#define SMBreadBs 0x1C /* read block (secondary response) */ +#define SMBwriteBraw 0x1D /* write block raw */ +#define SMBwriteBmpx 0x1E /* write block multiplexed */ +#define SMBwriteBs 0x1F /* write block (secondary request) */ +#define SMBwriteC 0x20 /* write complete response */ +#define SMBsetattrE 0x22 /* set file attributes expanded */ +#define SMBgetattrE 0x23 /* get file attributes expanded */ +#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */ +#define SMBtrans 0x25 /* transaction - name, bytes in/out */ +#define SMBtranss 0x26 /* transaction (secondary request/response) */ +#define SMBioctl 0x27 /* IOCTL */ +#define SMBioctls 0x28 /* IOCTL (secondary request/response) */ +#define SMBcopy 0x29 /* copy */ +#define SMBmove 0x2A /* move */ +#define SMBecho 0x2B /* echo */ +#define SMBopenX 0x2D /* open and X */ +#define SMBreadX 0x2E /* read and X */ +#define SMBwriteX 0x2F /* write and X */ +#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */ +#define SMBtconX 0x75 /* tree connect and X */ +#define SMBffirst 0x82 /* find first */ +#define SMBfunique 0x83 /* find unique */ +#define SMBfclose 0x84 /* find close */ +#define SMBinvalid 0xFE /* invalid command */ + + +/* Extended 2.0 protocol */ +#define SMBtrans2 0x32 /* TRANS2 protocol set */ +#define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */ +#define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */ +#define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */ +#define SMBulogoffX 0x74 /* user logoff */ + +/* these are the TRANS2 sub commands */ +#define TRANSACT2_OPEN 0 +#define TRANSACT2_FINDFIRST 1 +#define TRANSACT2_FINDNEXT 2 +#define TRANSACT2_QFSINFO 3 +#define TRANSACT2_SETFSINFO 4 +#define TRANSACT2_QPATHINFO 5 +#define TRANSACT2_SETPATHINFO 6 +#define TRANSACT2_QFILEINFO 7 +#define TRANSACT2_SETFILEINFO 8 +#define TRANSACT2_FSCTL 9 +#define TRANSACT2_IOCTL 10 +#define TRANSACT2_FINDNOTIFYFIRST 11 +#define TRANSACT2_FINDNOTIFYNEXT 12 +#define TRANSACT2_MKDIR 13 + +/* Information Levels - Shared? */ +#define SMB_INFO_STANDARD 1 +#define SMB_INFO_QUERY_EA_SIZE 2 +#define SMB_INFO_QUERY_EAS_FROM_LIST 3 +#define SMB_INFO_QUERY_ALL_EAS 4 +#define SMB_INFO_IS_NAME_VALID 6 + +/* Information Levels - TRANSACT2_FINDFIRST */ +#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 +#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_FILE_NAMES_INFO 0x103 +#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 + +/* Information Levels - TRANSACT2_QPATHINFO */ +#define SMB_QUERY_FILE_BASIC_INFO 0x101 +#define SMB_QUERY_FILE_STANDARD_INFO 0x102 +#define SMB_QUERY_FILE_EA_INFO 0x103 +#define SMB_QUERY_FILE_NAME_INFO 0x104 +#define SMB_QUERY_FILE_ALL_INFO 0x107 +#define SMB_QUERY_FILE_ALT_NAME_INFO 0x108 +#define SMB_QUERY_FILE_STREAM_INFO 0x109 +#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10b + +/* Information Levels - TRANSACT2_SETFILEINFO */ +#define SMB_SET_FILE_BASIC_INFO 0x101 +#define SMB_SET_FILE_DISPOSITION_INFO 0x102 +#define SMB_SET_FILE_ALLOCATION_INFO 0x103 +#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 + +/* smb_flg field flags */ +#define SMB_FLAGS_SUPPORT_LOCKREAD 0x01 +#define SMB_FLAGS_CLIENT_BUF_AVAIL 0x02 +#define SMB_FLAGS_RESERVED 0x04 +#define SMB_FLAGS_CASELESS_PATHNAMES 0x08 +#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10 +#define SMB_FLAGS_REQUEST_OPLOCK 0x20 +#define SMB_FLAGS_REQUEST_BATCH_OPLOCK 0x40 +#define SMB_FLAGS_REPLY 0x80 + +/* smb_flg2 field flags (samba-2.2.0/source/include/smb.h) */ +#define SMB_FLAGS2_LONG_PATH_COMPONENTS 0x0001 +#define SMB_FLAGS2_EXTENDED_ATTRIBUTES 0x0002 +#define SMB_FLAGS2_DFS_PATHNAMES 0x1000 +#define SMB_FLAGS2_READ_PERMIT_NO_EXECUTE 0x2000 +#define SMB_FLAGS2_32_BIT_ERROR_CODES 0x4000 +#define SMB_FLAGS2_UNICODE_STRINGS 0x8000 + + +/* + * UNIX stuff (from samba trans2.h) + */ +#define MIN_UNIX_INFO_LEVEL 0x200 +#define MAX_UNIX_INFO_LEVEL 0x2FF +#define SMB_FIND_FILE_UNIX 0x202 +#define SMB_QUERY_FILE_UNIX_BASIC 0x200 +#define SMB_QUERY_FILE_UNIX_LINK 0x201 +#define SMB_QUERY_FILE_UNIX_HLINK 0x202 +#define SMB_SET_FILE_UNIX_BASIC 0x200 +#define SMB_SET_FILE_UNIX_LINK 0x201 +#define SMB_SET_FILE_UNIX_HLINK 0x203 +#define SMB_QUERY_CIFS_UNIX_INFO 0x200 + +/* values which means "don't change it" */ +#define SMB_MODE_NO_CHANGE 0xFFFFFFFF +#define SMB_UID_NO_CHANGE 0xFFFFFFFF +#define SMB_GID_NO_CHANGE 0xFFFFFFFF +#define SMB_TIME_NO_CHANGE 0xFFFFFFFFFFFFFFFFULL +#define SMB_SIZE_NO_CHANGE 0xFFFFFFFFFFFFFFFFULL + +/* UNIX filetype mappings. */ +#define UNIX_TYPE_FILE 0 +#define UNIX_TYPE_DIR 1 +#define UNIX_TYPE_SYMLINK 2 +#define UNIX_TYPE_CHARDEV 3 +#define UNIX_TYPE_BLKDEV 4 +#define UNIX_TYPE_FIFO 5 +#define UNIX_TYPE_SOCKET 6 +#define UNIX_TYPE_UNKNOWN 0xFFFFFFFF + +#endif /* _SMBNO_H_ */ diff --git a/drivers/staging/smbfs/sock.c b/drivers/staging/smbfs/sock.c new file mode 100644 index 000000000000..9e264090e611 --- /dev/null +++ b/drivers/staging/smbfs/sock.c @@ -0,0 +1,385 @@ +/* + * sock.c + * + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "smb_fs.h" +#include "smb.h" +#include "smbno.h" +#include "smb_debug.h" +#include "proto.h" +#include "request.h" + + +static int +_recvfrom(struct socket *socket, unsigned char *ubuf, int size, unsigned flags) +{ + struct kvec iov = {ubuf, size}; + struct msghdr msg = {.msg_flags = flags}; + msg.msg_flags |= MSG_DONTWAIT | MSG_NOSIGNAL; + return kernel_recvmsg(socket, &msg, &iov, 1, size, msg.msg_flags); +} + +/* + * Return the server this socket belongs to + */ +static struct smb_sb_info * +server_from_socket(struct socket *socket) +{ + return socket->sk->sk_user_data; +} + +/* + * Called when there is data on the socket. + */ +void +smb_data_ready(struct sock *sk, int len) +{ + struct smb_sb_info *server = server_from_socket(sk->sk_socket); + void (*data_ready)(struct sock *, int) = server->data_ready; + + data_ready(sk, len); + VERBOSE("(%p, %d)\n", sk, len); + smbiod_wake_up(); +} + +int +smb_valid_socket(struct inode * inode) +{ + return (inode && S_ISSOCK(inode->i_mode) && + SOCKET_I(inode)->type == SOCK_STREAM); +} + +static struct socket * +server_sock(struct smb_sb_info *server) +{ + struct file *file; + + if (server && (file = server->sock_file)) + { +#ifdef SMBFS_PARANOIA + if (!smb_valid_socket(file->f_path.dentry->d_inode)) + PARANOIA("bad socket!\n"); +#endif + return SOCKET_I(file->f_path.dentry->d_inode); + } + return NULL; +} + +void +smb_close_socket(struct smb_sb_info *server) +{ + struct file * file = server->sock_file; + + if (file) { + struct socket *sock = server_sock(server); + + VERBOSE("closing socket %p\n", sock); + sock->sk->sk_data_ready = server->data_ready; + server->sock_file = NULL; + fput(file); + } +} + +static int +smb_get_length(struct socket *socket, unsigned char *header) +{ + int result; + + result = _recvfrom(socket, header, 4, MSG_PEEK); + if (result == -EAGAIN) + return -ENODATA; + if (result < 0) { + PARANOIA("recv error = %d\n", -result); + return result; + } + if (result < 4) + return -ENODATA; + + switch (header[0]) { + case 0x00: + case 0x82: + break; + + case 0x85: + DEBUG1("Got SESSION KEEP ALIVE\n"); + _recvfrom(socket, header, 4, 0); /* read away */ + return -ENODATA; + + default: + PARANOIA("Invalid NBT packet, code=%x\n", header[0]); + return -EIO; + } + + /* The length in the RFC NB header is the raw data length */ + return smb_len(header); +} + +int +smb_recv_available(struct smb_sb_info *server) +{ + mm_segment_t oldfs; + int avail, err; + struct socket *sock = server_sock(server); + + oldfs = get_fs(); + set_fs(get_ds()); + err = sock->ops->ioctl(sock, SIOCINQ, (unsigned long) &avail); + set_fs(oldfs); + return (err >= 0) ? avail : err; +} + +/* + * Adjust the kvec to move on 'n' bytes (from nfs/sunrpc) + */ +static int +smb_move_iov(struct kvec **data, size_t *num, struct kvec *vec, unsigned amount) +{ + struct kvec *iv = *data; + int i; + int len; + + /* + * Eat any sent kvecs + */ + while (iv->iov_len <= amount) { + amount -= iv->iov_len; + iv++; + (*num)--; + } + + /* + * And chew down the partial one + */ + vec[0].iov_len = iv->iov_len-amount; + vec[0].iov_base =((unsigned char *)iv->iov_base)+amount; + iv++; + + len = vec[0].iov_len; + + /* + * And copy any others + */ + for (i = 1; i < *num; i++) { + vec[i] = *iv++; + len += vec[i].iov_len; + } + + *data = vec; + return len; +} + +/* + * smb_receive_header + * Only called by the smbiod thread. + */ +int +smb_receive_header(struct smb_sb_info *server) +{ + struct socket *sock; + int result = 0; + unsigned char peek_buf[4]; + + result = -EIO; + sock = server_sock(server); + if (!sock) + goto out; + if (sock->sk->sk_state != TCP_ESTABLISHED) + goto out; + + if (!server->smb_read) { + result = smb_get_length(sock, peek_buf); + if (result < 0) { + if (result == -ENODATA) + result = 0; + goto out; + } + server->smb_len = result + 4; + + if (server->smb_len < SMB_HEADER_LEN) { + PARANOIA("short packet: %d\n", result); + server->rstate = SMB_RECV_DROP; + result = -EIO; + goto out; + } + if (server->smb_len > SMB_MAX_PACKET_SIZE) { + PARANOIA("long packet: %d\n", result); + server->rstate = SMB_RECV_DROP; + result = -EIO; + goto out; + } + } + + result = _recvfrom(sock, server->header + server->smb_read, + SMB_HEADER_LEN - server->smb_read, 0); + VERBOSE("_recvfrom: %d\n", result); + if (result < 0) { + VERBOSE("receive error: %d\n", result); + goto out; + } + server->smb_read += result; + + if (server->smb_read == SMB_HEADER_LEN) + server->rstate = SMB_RECV_HCOMPLETE; +out: + return result; +} + +static char drop_buffer[PAGE_SIZE]; + +/* + * smb_receive_drop - read and throw away the data + * Only called by the smbiod thread. + * + * FIXME: we are in the kernel, could we just tell the socket that we want + * to drop stuff from the buffer? + */ +int +smb_receive_drop(struct smb_sb_info *server) +{ + struct socket *sock; + unsigned int flags; + struct kvec iov; + struct msghdr msg; + int rlen = smb_len(server->header) - server->smb_read + 4; + int result = -EIO; + + if (rlen > PAGE_SIZE) + rlen = PAGE_SIZE; + + sock = server_sock(server); + if (!sock) + goto out; + if (sock->sk->sk_state != TCP_ESTABLISHED) + goto out; + + flags = MSG_DONTWAIT | MSG_NOSIGNAL; + iov.iov_base = drop_buffer; + iov.iov_len = PAGE_SIZE; + msg.msg_flags = flags; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + + result = kernel_recvmsg(sock, &msg, &iov, 1, rlen, flags); + + VERBOSE("read: %d\n", result); + if (result < 0) { + VERBOSE("receive error: %d\n", result); + goto out; + } + server->smb_read += result; + + if (server->smb_read >= server->smb_len) + server->rstate = SMB_RECV_END; + +out: + return result; +} + +/* + * smb_receive + * Only called by the smbiod thread. + */ +int +smb_receive(struct smb_sb_info *server, struct smb_request *req) +{ + struct socket *sock; + unsigned int flags; + struct kvec iov[4]; + struct kvec *p = req->rq_iov; + size_t num = req->rq_iovlen; + struct msghdr msg; + int rlen; + int result = -EIO; + + sock = server_sock(server); + if (!sock) + goto out; + if (sock->sk->sk_state != TCP_ESTABLISHED) + goto out; + + flags = MSG_DONTWAIT | MSG_NOSIGNAL; + msg.msg_flags = flags; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + + /* Dont repeat bytes and count available bufferspace */ + rlen = min_t(int, smb_move_iov(&p, &num, iov, req->rq_bytes_recvd), + (req->rq_rlen - req->rq_bytes_recvd)); + + result = kernel_recvmsg(sock, &msg, p, num, rlen, flags); + + VERBOSE("read: %d\n", result); + if (result < 0) { + VERBOSE("receive error: %d\n", result); + goto out; + } + req->rq_bytes_recvd += result; + server->smb_read += result; + +out: + return result; +} + +/* + * Try to send a SMB request. This may return after sending only parts of the + * request. SMB_REQ_TRANSMITTED will be set if a request was fully sent. + * + * Parts of this was taken from xprt_sendmsg from net/sunrpc/xprt.c + */ +int +smb_send_request(struct smb_request *req) +{ + struct smb_sb_info *server = req->rq_server; + struct socket *sock; + struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT}; + int slen = req->rq_slen - req->rq_bytes_sent; + int result = -EIO; + struct kvec iov[4]; + struct kvec *p = req->rq_iov; + size_t num = req->rq_iovlen; + + sock = server_sock(server); + if (!sock) + goto out; + if (sock->sk->sk_state != TCP_ESTABLISHED) + goto out; + + /* Dont repeat bytes */ + if (req->rq_bytes_sent) + smb_move_iov(&p, &num, iov, req->rq_bytes_sent); + + result = kernel_sendmsg(sock, &msg, p, num, slen); + + if (result >= 0) { + req->rq_bytes_sent += result; + if (req->rq_bytes_sent >= req->rq_slen) + req->rq_flags |= SMB_REQ_TRANSMITTED; + } +out: + return result; +} diff --git a/drivers/staging/smbfs/symlink.c b/drivers/staging/smbfs/symlink.c new file mode 100644 index 000000000000..632c4acd062d --- /dev/null +++ b/drivers/staging/smbfs/symlink.c @@ -0,0 +1,67 @@ +/* + * symlink.c + * + * Copyright (C) 2002 by John Newbigin + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "smbno.h" +#include "smb_fs.h" +#include "smb_debug.h" +#include "proto.h" + +int smb_symlink(struct inode *inode, struct dentry *dentry, const char *oldname) +{ + DEBUG1("create symlink %s -> %s/%s\n", oldname, DENTRY_PATH(dentry)); + + return smb_proc_symlink(server_from_dentry(dentry), dentry, oldname); +} + +static void *smb_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char *link = __getname(); + DEBUG1("followlink of %s/%s\n", DENTRY_PATH(dentry)); + + if (!link) { + link = ERR_PTR(-ENOMEM); + } else { + int len = smb_proc_read_link(server_from_dentry(dentry), + dentry, link, PATH_MAX - 1); + if (len < 0) { + __putname(link); + link = ERR_PTR(len); + } else { + link[len] = 0; + } + } + nd_set_link(nd, link); + return NULL; +} + +static void smb_put_link(struct dentry *dentry, struct nameidata *nd, void *p) +{ + char *s = nd_get_link(nd); + if (!IS_ERR(s)) + __putname(s); +} + +const struct inode_operations smb_link_inode_operations = +{ + .readlink = generic_readlink, + .follow_link = smb_follow_link, + .put_link = smb_put_link, +}; diff --git a/fs/Kconfig b/fs/Kconfig index 30da8ee16a96..25ce2dc1c6d4 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -233,7 +233,6 @@ config NFS_COMMON default y source "net/sunrpc/Kconfig" -source "fs/smbfs/Kconfig" source "fs/ceph/Kconfig" source "fs/cifs/Kconfig" source "fs/ncpfs/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index e571feddd7b7..9284c74c2db9 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -91,7 +91,6 @@ obj-$(CONFIG_NFSD) += nfsd/ obj-$(CONFIG_LOCKD) += lockd/ obj-$(CONFIG_NLS) += nls/ obj-$(CONFIG_SYSV_FS) += sysv/ -obj-$(CONFIG_SMB_FS) += smbfs/ obj-$(CONFIG_CIFS) += cifs/ obj-$(CONFIG_NCP_FS) += ncpfs/ obj-$(CONFIG_HPFS_FS) += hpfs/ diff --git a/fs/compat.c b/fs/compat.c index 718c7062aec1..b42f29a44edb 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -29,8 +29,6 @@ #include #include #include -#include -#include #include #include #include @@ -745,30 +743,6 @@ static void *do_ncp_super_data_conv(void *raw_data) return raw_data; } -struct compat_smb_mount_data { - compat_int_t version; - __compat_uid_t mounted_uid; - __compat_uid_t uid; - __compat_gid_t gid; - compat_mode_t file_mode; - compat_mode_t dir_mode; -}; - -static void *do_smb_super_data_conv(void *raw_data) -{ - struct smb_mount_data *s = raw_data; - struct compat_smb_mount_data *c_s = raw_data; - - if (c_s->version != SMB_MOUNT_OLDVERSION) - goto out; - s->dir_mode = c_s->dir_mode; - s->file_mode = c_s->file_mode; - s->gid = c_s->gid; - s->uid = c_s->uid; - s->mounted_uid = c_s->mounted_uid; - out: - return raw_data; -} struct compat_nfs_string { compat_uint_t len; @@ -835,7 +809,6 @@ static int do_nfs4_super_data_conv(void *raw_data) return 0; } -#define SMBFS_NAME "smbfs" #define NCPFS_NAME "ncpfs" #define NFS4_NAME "nfs4" @@ -870,9 +843,7 @@ asmlinkage long compat_sys_mount(const char __user * dev_name, retval = -EINVAL; if (kernel_type && data_page) { - if (!strcmp(kernel_type, SMBFS_NAME)) { - do_smb_super_data_conv((void *)data_page); - } else if (!strcmp(kernel_type, NCPFS_NAME)) { + if (!strcmp(kernel_type, NCPFS_NAME)) { do_ncp_super_data_conv((void *)data_page); } else if (!strcmp(kernel_type, NFS4_NAME)) { if (do_nfs4_super_data_conv((void *) data_page)) diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 03e59aa318eb..34cf03cd791f 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include @@ -558,25 +557,6 @@ static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, void __user *argp) #endif /* CONFIG_BLOCK */ -static int do_smb_getmountuid(unsigned int fd, unsigned int cmd, - compat_uid_t __user *argp) -{ - mm_segment_t old_fs = get_fs(); - __kernel_uid_t kuid; - int err; - - cmd = SMB_IOC_GETMOUNTUID; - - set_fs(KERNEL_DS); - err = sys_ioctl(fd, cmd, (unsigned long)&kuid); - set_fs(old_fs); - - if (err >= 0) - err = put_user(kuid, argp); - - return err; -} - /* Bluetooth ioctls */ #define HCIUARTSETPROTO _IOW('U', 200, int) #define HCIUARTGETPROTO _IOR('U', 201, int) @@ -1265,8 +1245,6 @@ COMPATIBLE_IOCTL(OSS_GETVERSION) /* Raw devices */ COMPATIBLE_IOCTL(RAW_SETBIND) COMPATIBLE_IOCTL(RAW_GETBIND) -/* SMB ioctls which do not need any translations */ -COMPATIBLE_IOCTL(SMB_IOC_NEWCONN) /* Watchdog */ COMPATIBLE_IOCTL(WDIOC_GETSUPPORT) COMPATIBLE_IOCTL(WDIOC_GETSTATUS) @@ -1528,10 +1506,6 @@ static long do_ioctl_trans(int fd, unsigned int cmd, case RAW_GETBIND: return raw_ioctl(fd, cmd, argp); #endif - /* One SMB ioctl needs translations. */ -#define SMB_IOC_GETMOUNTUID_32 _IOR('u', 1, compat_uid_t) - case SMB_IOC_GETMOUNTUID_32: - return do_smb_getmountuid(fd, cmd, argp); /* Serial */ case TIOCGSERIAL: case TIOCSSERIAL: diff --git a/fs/smbfs/Kconfig b/fs/smbfs/Kconfig deleted file mode 100644 index e668127c8b2e..000000000000 --- a/fs/smbfs/Kconfig +++ /dev/null @@ -1,55 +0,0 @@ -config SMB_FS - tristate "SMB file system support (OBSOLETE, please use CIFS)" - depends on INET - select NLS - help - SMB (Server Message Block) is the protocol Windows for Workgroups - (WfW), Windows 95/98, Windows NT and OS/2 Lan Manager use to share - files and printers over local networks. Saying Y here allows you to - mount their file systems (often called "shares" in this context) and - access them just like any other Unix directory. Currently, this - works only if the Windows machines use TCP/IP as the underlying - transport protocol, and not NetBEUI. For details, read - and the SMB-HOWTO, - available from . - - Note: if you just want your box to act as an SMB *server* and make - files and printing services available to Windows clients (which need - to have a TCP/IP stack), you don't need to say Y here; you can use - the program SAMBA (available from ) - for that. - - General information about how to connect Linux, Windows machines and - Macs is on the WWW at . - - To compile the SMB support as a module, choose M here: - the module will be called smbfs. Most people say N, however. - -config SMB_NLS_DEFAULT - bool "Use a default NLS" - depends on SMB_FS - help - Enabling this will make smbfs use nls translations by default. You - need to specify the local charset (CONFIG_NLS_DEFAULT) in the nls - settings and you need to give the default nls for the SMB server as - CONFIG_SMB_NLS_REMOTE. - - The nls settings can be changed at mount time, if your smbmount - supports that, using the codepage and iocharset parameters. - - smbmount from samba 2.2.0 or later supports this. - -config SMB_NLS_REMOTE - string "Default Remote NLS Option" - depends on SMB_NLS_DEFAULT - default "cp437" - help - This setting allows you to specify a default value for which - codepage the server uses. If this field is left blank no - translations will be done by default. The local codepage/charset - default to CONFIG_NLS_DEFAULT. - - The nls settings can be changed at mount time, if your smbmount - supports that, using the codepage and iocharset parameters. - - smbmount from samba 2.2.0 or later supports this. diff --git a/fs/smbfs/Makefile b/fs/smbfs/Makefile deleted file mode 100644 index 4faf8c4722c3..000000000000 --- a/fs/smbfs/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# -# Makefile for the linux smb-filesystem routines. -# - -obj-$(CONFIG_SMB_FS) += smbfs.o - -smbfs-objs := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o \ - symlink.o smbiod.o request.o - -# If you want debugging output, you may add these flags to the EXTRA_CFLAGS -# SMBFS_PARANOIA should normally be enabled. - -EXTRA_CFLAGS += -DSMBFS_PARANOIA -#EXTRA_CFLAGS += -DSMBFS_DEBUG -#EXTRA_CFLAGS += -DSMBFS_DEBUG_VERBOSE -#EXTRA_CFLAGS += -DDEBUG_SMB_TIMESTAMP -#EXTRA_CFLAGS += -Werror - diff --git a/fs/smbfs/cache.c b/fs/smbfs/cache.c deleted file mode 100644 index 8c177eb7e344..000000000000 --- a/fs/smbfs/cache.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * cache.c - * - * Copyright (C) 1997 by Bill Hawes - * - * Routines to support directory cacheing using the page cache. - * This cache code is almost directly taken from ncpfs. - * - * Please add a note about your changes to smbfs in the ChangeLog file. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "smb_debug.h" -#include "proto.h" - -/* - * Force the next attempt to use the cache to be a timeout. - * If we can't find the page that's fine, it will cause a refresh. - */ -void -smb_invalid_dir_cache(struct inode * dir) -{ - struct smb_sb_info *server = server_from_inode(dir); - union smb_dir_cache *cache = NULL; - struct page *page = NULL; - - page = grab_cache_page(&dir->i_data, 0); - if (!page) - goto out; - - if (!PageUptodate(page)) - goto out_unlock; - - cache = kmap(page); - cache->head.time = jiffies - SMB_MAX_AGE(server); - - kunmap(page); - SetPageUptodate(page); -out_unlock: - unlock_page(page); - page_cache_release(page); -out: - return; -} - -/* - * Mark all dentries for 'parent' as invalid, forcing them to be re-read - */ -void -smb_invalidate_dircache_entries(struct dentry *parent) -{ - struct smb_sb_info *server = server_from_dentry(parent); - struct list_head *next; - struct dentry *dentry; - - spin_lock(&dcache_lock); - next = parent->d_subdirs.next; - while (next != &parent->d_subdirs) { - dentry = list_entry(next, struct dentry, d_u.d_child); - dentry->d_fsdata = NULL; - smb_age_dentry(server, dentry); - next = next->next; - } - spin_unlock(&dcache_lock); -} - -/* - * dget, but require that fpos and parent matches what the dentry contains. - * dentry is not known to be a valid pointer at entry. - */ -struct dentry * -smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) -{ - struct dentry *dent = dentry; - struct list_head *next; - - if (d_validate(dent, parent)) { - if (dent->d_name.len <= SMB_MAXNAMELEN && - (unsigned long)dent->d_fsdata == fpos) { - if (!dent->d_inode) { - dput(dent); - dent = NULL; - } - return dent; - } - dput(dent); - } - - /* If a pointer is invalid, we search the dentry. */ - spin_lock(&dcache_lock); - next = parent->d_subdirs.next; - while (next != &parent->d_subdirs) { - dent = list_entry(next, struct dentry, d_u.d_child); - if ((unsigned long)dent->d_fsdata == fpos) { - if (dent->d_inode) - dget_locked(dent); - else - dent = NULL; - goto out_unlock; - } - next = next->next; - } - dent = NULL; -out_unlock: - spin_unlock(&dcache_lock); - return dent; -} - - -/* - * Create dentry/inode for this file and add it to the dircache. - */ -int -smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, - struct smb_cache_control *ctrl, struct qstr *qname, - struct smb_fattr *entry) -{ - struct dentry *newdent, *dentry = filp->f_path.dentry; - struct inode *newino, *inode = dentry->d_inode; - struct smb_cache_control ctl = *ctrl; - int valid = 0; - int hashed = 0; - ino_t ino = 0; - - qname->hash = full_name_hash(qname->name, qname->len); - - if (dentry->d_op && dentry->d_op->d_hash) - if (dentry->d_op->d_hash(dentry, qname) != 0) - goto end_advance; - - newdent = d_lookup(dentry, qname); - - if (!newdent) { - newdent = d_alloc(dentry, qname); - if (!newdent) - goto end_advance; - } else { - hashed = 1; - memcpy((char *) newdent->d_name.name, qname->name, - newdent->d_name.len); - } - - if (!newdent->d_inode) { - smb_renew_times(newdent); - entry->f_ino = iunique(inode->i_sb, 2); - newino = smb_iget(inode->i_sb, entry); - if (newino) { - smb_new_dentry(newdent); - d_instantiate(newdent, newino); - if (!hashed) - d_rehash(newdent); - } - } else - smb_set_inode_attr(newdent->d_inode, entry); - - if (newdent->d_inode) { - ino = newdent->d_inode->i_ino; - newdent->d_fsdata = (void *) ctl.fpos; - smb_new_dentry(newdent); - } - - if (ctl.idx >= SMB_DIRCACHE_SIZE) { - if (ctl.page) { - kunmap(ctl.page); - SetPageUptodate(ctl.page); - unlock_page(ctl.page); - page_cache_release(ctl.page); - } - ctl.cache = NULL; - ctl.idx -= SMB_DIRCACHE_SIZE; - ctl.ofs += 1; - ctl.page = grab_cache_page(&inode->i_data, ctl.ofs); - if (ctl.page) - ctl.cache = kmap(ctl.page); - } - if (ctl.cache) { - ctl.cache->dentry[ctl.idx] = newdent; - valid = 1; - } - dput(newdent); - -end_advance: - if (!valid) - ctl.valid = 0; - if (!ctl.filled && (ctl.fpos == filp->f_pos)) { - if (!ino) - ino = find_inode_number(dentry, qname); - if (!ino) - ino = iunique(inode->i_sb, 2); - ctl.filled = filldir(dirent, qname->name, qname->len, - filp->f_pos, ino, DT_UNKNOWN); - if (!ctl.filled) - filp->f_pos += 1; - } - ctl.fpos += 1; - ctl.idx += 1; - *ctrl = ctl; - return (ctl.valid || !ctl.filled); -} diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c deleted file mode 100644 index 00a70cab1f36..000000000000 --- a/fs/smbfs/dir.c +++ /dev/null @@ -1,702 +0,0 @@ -/* - * dir.c - * - * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - * Please add a note about your changes to smbfs in the ChangeLog file. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "smb_debug.h" -#include "proto.h" - -static int smb_readdir(struct file *, void *, filldir_t); -static int smb_dir_open(struct inode *, struct file *); - -static struct dentry *smb_lookup(struct inode *, struct dentry *, struct nameidata *); -static int smb_create(struct inode *, struct dentry *, int, struct nameidata *); -static int smb_mkdir(struct inode *, struct dentry *, int); -static int smb_rmdir(struct inode *, struct dentry *); -static int smb_unlink(struct inode *, struct dentry *); -static int smb_rename(struct inode *, struct dentry *, - struct inode *, struct dentry *); -static int smb_make_node(struct inode *,struct dentry *,int,dev_t); -static int smb_link(struct dentry *, struct inode *, struct dentry *); - -const struct file_operations smb_dir_operations = -{ - .llseek = generic_file_llseek, - .read = generic_read_dir, - .readdir = smb_readdir, - .unlocked_ioctl = smb_ioctl, - .open = smb_dir_open, -}; - -const struct inode_operations smb_dir_inode_operations = -{ - .create = smb_create, - .lookup = smb_lookup, - .unlink = smb_unlink, - .mkdir = smb_mkdir, - .rmdir = smb_rmdir, - .rename = smb_rename, - .getattr = smb_getattr, - .setattr = smb_notify_change, -}; - -const struct inode_operations smb_dir_inode_operations_unix = -{ - .create = smb_create, - .lookup = smb_lookup, - .unlink = smb_unlink, - .mkdir = smb_mkdir, - .rmdir = smb_rmdir, - .rename = smb_rename, - .getattr = smb_getattr, - .setattr = smb_notify_change, - .symlink = smb_symlink, - .mknod = smb_make_node, - .link = smb_link, -}; - -/* - * Read a directory, using filldir to fill the dirent memory. - * smb_proc_readdir does the actual reading from the smb server. - * - * The cache code is almost directly taken from ncpfs - */ -static int -smb_readdir(struct file *filp, void *dirent, filldir_t filldir) -{ - struct dentry *dentry = filp->f_path.dentry; - struct inode *dir = dentry->d_inode; - struct smb_sb_info *server = server_from_dentry(dentry); - union smb_dir_cache *cache = NULL; - struct smb_cache_control ctl; - struct page *page = NULL; - int result; - - ctl.page = NULL; - ctl.cache = NULL; - - VERBOSE("reading %s/%s, f_pos=%d\n", - DENTRY_PATH(dentry), (int) filp->f_pos); - - result = 0; - - lock_kernel(); - - switch ((unsigned int) filp->f_pos) { - case 0: - if (filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR) < 0) - goto out; - filp->f_pos = 1; - /* fallthrough */ - case 1: - if (filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR) < 0) - goto out; - filp->f_pos = 2; - } - - /* - * Make sure our inode is up-to-date. - */ - result = smb_revalidate_inode(dentry); - if (result) - goto out; - - - page = grab_cache_page(&dir->i_data, 0); - if (!page) - goto read_really; - - ctl.cache = cache = kmap(page); - ctl.head = cache->head; - - if (!PageUptodate(page) || !ctl.head.eof) { - VERBOSE("%s/%s, page uptodate=%d, eof=%d\n", - DENTRY_PATH(dentry), PageUptodate(page),ctl.head.eof); - goto init_cache; - } - - if (filp->f_pos == 2) { - if (jiffies - ctl.head.time >= SMB_MAX_AGE(server)) - goto init_cache; - - /* - * N.B. ncpfs checks mtime of dentry too here, we don't. - * 1. common smb servers do not update mtime on dir changes - * 2. it requires an extra smb request - * (revalidate has the same timeout as ctl.head.time) - * - * Instead smbfs invalidates its own cache on local changes - * and remote changes are not seen until timeout. - */ - } - - if (filp->f_pos > ctl.head.end) - goto finished; - - ctl.fpos = filp->f_pos + (SMB_DIRCACHE_START - 2); - ctl.ofs = ctl.fpos / SMB_DIRCACHE_SIZE; - ctl.idx = ctl.fpos % SMB_DIRCACHE_SIZE; - - for (;;) { - if (ctl.ofs != 0) { - ctl.page = find_lock_page(&dir->i_data, ctl.ofs); - if (!ctl.page) - goto invalid_cache; - ctl.cache = kmap(ctl.page); - if (!PageUptodate(ctl.page)) - goto invalid_cache; - } - while (ctl.idx < SMB_DIRCACHE_SIZE) { - struct dentry *dent; - int res; - - dent = smb_dget_fpos(ctl.cache->dentry[ctl.idx], - dentry, filp->f_pos); - if (!dent) - goto invalid_cache; - - res = filldir(dirent, dent->d_name.name, - dent->d_name.len, filp->f_pos, - dent->d_inode->i_ino, DT_UNKNOWN); - dput(dent); - if (res) - goto finished; - filp->f_pos += 1; - ctl.idx += 1; - if (filp->f_pos > ctl.head.end) - goto finished; - } - if (ctl.page) { - kunmap(ctl.page); - SetPageUptodate(ctl.page); - unlock_page(ctl.page); - page_cache_release(ctl.page); - ctl.page = NULL; - } - ctl.idx = 0; - ctl.ofs += 1; - } -invalid_cache: - if (ctl.page) { - kunmap(ctl.page); - unlock_page(ctl.page); - page_cache_release(ctl.page); - ctl.page = NULL; - } - ctl.cache = cache; -init_cache: - smb_invalidate_dircache_entries(dentry); - ctl.head.time = jiffies; - ctl.head.eof = 0; - ctl.fpos = 2; - ctl.ofs = 0; - ctl.idx = SMB_DIRCACHE_START; - ctl.filled = 0; - ctl.valid = 1; -read_really: - result = server->ops->readdir(filp, dirent, filldir, &ctl); - if (result == -ERESTARTSYS && page) - ClearPageUptodate(page); - if (ctl.idx == -1) - goto invalid_cache; /* retry */ - ctl.head.end = ctl.fpos - 1; - ctl.head.eof = ctl.valid; -finished: - if (page) { - cache->head = ctl.head; - kunmap(page); - if (result != -ERESTARTSYS) - SetPageUptodate(page); - unlock_page(page); - page_cache_release(page); - } - if (ctl.page) { - kunmap(ctl.page); - SetPageUptodate(ctl.page); - unlock_page(ctl.page); - page_cache_release(ctl.page); - } -out: - unlock_kernel(); - return result; -} - -static int -smb_dir_open(struct inode *dir, struct file *file) -{ - struct dentry *dentry = file->f_path.dentry; - struct smb_sb_info *server; - int error = 0; - - VERBOSE("(%s/%s)\n", dentry->d_parent->d_name.name, - file->f_path.dentry->d_name.name); - - /* - * Directory timestamps in the core protocol aren't updated - * when a file is added, so we give them a very short TTL. - */ - lock_kernel(); - server = server_from_dentry(dentry); - if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) { - unsigned long age = jiffies - SMB_I(dir)->oldmtime; - if (age > 2*HZ) - smb_invalid_dir_cache(dir); - } - - /* - * Note: in order to allow the smbmount process to open the - * mount point, we only revalidate if the connection is valid or - * if the process is trying to access something other than the root. - */ - if (server->state == CONN_VALID || !IS_ROOT(dentry)) - error = smb_revalidate_inode(dentry); - unlock_kernel(); - return error; -} - -/* - * Dentry operations routines - */ -static int smb_lookup_validate(struct dentry *, struct nameidata *); -static int smb_hash_dentry(struct dentry *, struct qstr *); -static int smb_compare_dentry(struct dentry *, struct qstr *, struct qstr *); -static int smb_delete_dentry(struct dentry *); - -static const struct dentry_operations smbfs_dentry_operations = -{ - .d_revalidate = smb_lookup_validate, - .d_hash = smb_hash_dentry, - .d_compare = smb_compare_dentry, - .d_delete = smb_delete_dentry, -}; - -static const struct dentry_operations smbfs_dentry_operations_case = -{ - .d_revalidate = smb_lookup_validate, - .d_delete = smb_delete_dentry, -}; - - -/* - * This is the callback when the dcache has a lookup hit. - */ -static int -smb_lookup_validate(struct dentry * dentry, struct nameidata *nd) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - struct inode * inode = dentry->d_inode; - unsigned long age = jiffies - dentry->d_time; - int valid; - - /* - * The default validation is based on dentry age: - * we believe in dentries for a few seconds. (But each - * successful server lookup renews the timestamp.) - */ - valid = (age <= SMB_MAX_AGE(server)); -#ifdef SMBFS_DEBUG_VERBOSE - if (!valid) - VERBOSE("%s/%s not valid, age=%lu\n", - DENTRY_PATH(dentry), age); -#endif - - if (inode) { - lock_kernel(); - if (is_bad_inode(inode)) { - PARANOIA("%s/%s has dud inode\n", DENTRY_PATH(dentry)); - valid = 0; - } else if (!valid) - valid = (smb_revalidate_inode(dentry) == 0); - unlock_kernel(); - } else { - /* - * What should we do for negative dentries? - */ - } - return valid; -} - -static int -smb_hash_dentry(struct dentry *dir, struct qstr *this) -{ - unsigned long hash; - int i; - - hash = init_name_hash(); - for (i=0; i < this->len ; i++) - hash = partial_name_hash(tolower(this->name[i]), hash); - this->hash = end_name_hash(hash); - - return 0; -} - -static int -smb_compare_dentry(struct dentry *dir, struct qstr *a, struct qstr *b) -{ - int i, result = 1; - - if (a->len != b->len) - goto out; - for (i=0; i < a->len; i++) { - if (tolower(a->name[i]) != tolower(b->name[i])) - goto out; - } - result = 0; -out: - return result; -} - -/* - * This is the callback from dput() when d_count is going to 0. - * We use this to unhash dentries with bad inodes. - */ -static int -smb_delete_dentry(struct dentry * dentry) -{ - if (dentry->d_inode) { - if (is_bad_inode(dentry->d_inode)) { - PARANOIA("bad inode, unhashing %s/%s\n", - DENTRY_PATH(dentry)); - return 1; - } - } else { - /* N.B. Unhash negative dentries? */ - } - return 0; -} - -/* - * Initialize a new dentry - */ -void -smb_new_dentry(struct dentry *dentry) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - - if (server->mnt->flags & SMB_MOUNT_CASE) - dentry->d_op = &smbfs_dentry_operations_case; - else - dentry->d_op = &smbfs_dentry_operations; - dentry->d_time = jiffies; -} - - -/* - * Whenever a lookup succeeds, we know the parent directories - * are all valid, so we want to update the dentry timestamps. - * N.B. Move this to dcache? - */ -void -smb_renew_times(struct dentry * dentry) -{ - dget(dentry); - spin_lock(&dentry->d_lock); - for (;;) { - struct dentry *parent; - - dentry->d_time = jiffies; - if (IS_ROOT(dentry)) - break; - parent = dentry->d_parent; - dget(parent); - spin_unlock(&dentry->d_lock); - dput(dentry); - dentry = parent; - spin_lock(&dentry->d_lock); - } - spin_unlock(&dentry->d_lock); - dput(dentry); -} - -static struct dentry * -smb_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) -{ - struct smb_fattr finfo; - struct inode *inode; - int error; - struct smb_sb_info *server; - - error = -ENAMETOOLONG; - if (dentry->d_name.len > SMB_MAXNAMELEN) - goto out; - - /* Do not allow lookup of names with backslashes in */ - error = -EINVAL; - if (memchr(dentry->d_name.name, '\\', dentry->d_name.len)) - goto out; - - lock_kernel(); - error = smb_proc_getattr(dentry, &finfo); -#ifdef SMBFS_PARANOIA - if (error && error != -ENOENT) - PARANOIA("find %s/%s failed, error=%d\n", - DENTRY_PATH(dentry), error); -#endif - - inode = NULL; - if (error == -ENOENT) - goto add_entry; - if (!error) { - error = -EACCES; - finfo.f_ino = iunique(dentry->d_sb, 2); - inode = smb_iget(dir->i_sb, &finfo); - if (inode) { - add_entry: - server = server_from_dentry(dentry); - if (server->mnt->flags & SMB_MOUNT_CASE) - dentry->d_op = &smbfs_dentry_operations_case; - else - dentry->d_op = &smbfs_dentry_operations; - - d_add(dentry, inode); - smb_renew_times(dentry); - error = 0; - } - } - unlock_kernel(); -out: - return ERR_PTR(error); -} - -/* - * This code is common to all routines creating a new inode. - */ -static int -smb_instantiate(struct dentry *dentry, __u16 fileid, int have_id) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - struct inode *inode; - int error; - struct smb_fattr fattr; - - VERBOSE("file %s/%s, fileid=%u\n", DENTRY_PATH(dentry), fileid); - - error = smb_proc_getattr(dentry, &fattr); - if (error) - goto out_close; - - smb_renew_times(dentry); - fattr.f_ino = iunique(dentry->d_sb, 2); - inode = smb_iget(dentry->d_sb, &fattr); - if (!inode) - goto out_no_inode; - - if (have_id) { - struct smb_inode_info *ei = SMB_I(inode); - ei->fileid = fileid; - ei->access = SMB_O_RDWR; - ei->open = server->generation; - } - d_instantiate(dentry, inode); -out: - return error; - -out_no_inode: - error = -EACCES; -out_close: - if (have_id) { - PARANOIA("%s/%s failed, error=%d, closing %u\n", - DENTRY_PATH(dentry), error, fileid); - smb_close_fileid(dentry, fileid); - } - goto out; -} - -/* N.B. How should the mode argument be used? */ -static int -smb_create(struct inode *dir, struct dentry *dentry, int mode, - struct nameidata *nd) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - __u16 fileid; - int error; - struct iattr attr; - - VERBOSE("creating %s/%s, mode=%d\n", DENTRY_PATH(dentry), mode); - - lock_kernel(); - smb_invalid_dir_cache(dir); - error = smb_proc_create(dentry, 0, get_seconds(), &fileid); - if (!error) { - if (server->opt.capabilities & SMB_CAP_UNIX) { - /* Set attributes for new file */ - attr.ia_valid = ATTR_MODE; - attr.ia_mode = mode; - error = smb_proc_setattr_unix(dentry, &attr, 0, 0); - } - error = smb_instantiate(dentry, fileid, 1); - } else { - PARANOIA("%s/%s failed, error=%d\n", - DENTRY_PATH(dentry), error); - } - unlock_kernel(); - return error; -} - -/* N.B. How should the mode argument be used? */ -static int -smb_mkdir(struct inode *dir, struct dentry *dentry, int mode) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - int error; - struct iattr attr; - - lock_kernel(); - smb_invalid_dir_cache(dir); - error = smb_proc_mkdir(dentry); - if (!error) { - if (server->opt.capabilities & SMB_CAP_UNIX) { - /* Set attributes for new directory */ - attr.ia_valid = ATTR_MODE; - attr.ia_mode = mode; - error = smb_proc_setattr_unix(dentry, &attr, 0, 0); - } - error = smb_instantiate(dentry, 0, 0); - } - unlock_kernel(); - return error; -} - -static int -smb_rmdir(struct inode *dir, struct dentry *dentry) -{ - struct inode *inode = dentry->d_inode; - int error; - - /* - * Close the directory if it's open. - */ - lock_kernel(); - smb_close(inode); - - /* - * Check that nobody else is using the directory.. - */ - error = -EBUSY; - if (!d_unhashed(dentry)) - goto out; - - smb_invalid_dir_cache(dir); - error = smb_proc_rmdir(dentry); - -out: - unlock_kernel(); - return error; -} - -static int -smb_unlink(struct inode *dir, struct dentry *dentry) -{ - int error; - - /* - * Close the file if it's open. - */ - lock_kernel(); - smb_close(dentry->d_inode); - - smb_invalid_dir_cache(dir); - error = smb_proc_unlink(dentry); - if (!error) - smb_renew_times(dentry); - unlock_kernel(); - return error; -} - -static int -smb_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - int error; - - /* - * Close any open files, and check whether to delete the - * target before attempting the rename. - */ - lock_kernel(); - if (old_dentry->d_inode) - smb_close(old_dentry->d_inode); - if (new_dentry->d_inode) { - smb_close(new_dentry->d_inode); - error = smb_proc_unlink(new_dentry); - if (error) { - VERBOSE("unlink %s/%s, error=%d\n", - DENTRY_PATH(new_dentry), error); - goto out; - } - /* FIXME */ - d_delete(new_dentry); - } - - smb_invalid_dir_cache(old_dir); - smb_invalid_dir_cache(new_dir); - error = smb_proc_mv(old_dentry, new_dentry); - if (!error) { - smb_renew_times(old_dentry); - smb_renew_times(new_dentry); - } -out: - unlock_kernel(); - return error; -} - -/* - * FIXME: samba servers won't let you create device nodes unless uid/gid - * matches the connection credentials (and we don't know which those are ...) - */ -static int -smb_make_node(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) -{ - int error; - struct iattr attr; - - attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID; - attr.ia_mode = mode; - current_euid_egid(&attr.ia_uid, &attr.ia_gid); - - if (!new_valid_dev(dev)) - return -EINVAL; - - smb_invalid_dir_cache(dir); - error = smb_proc_setattr_unix(dentry, &attr, MAJOR(dev), MINOR(dev)); - if (!error) { - error = smb_instantiate(dentry, 0, 0); - } - return error; -} - -/* - * dentry = existing file - * new_dentry = new file - */ -static int -smb_link(struct dentry *dentry, struct inode *dir, struct dentry *new_dentry) -{ - int error; - - DEBUG1("smb_link old=%s/%s new=%s/%s\n", - DENTRY_PATH(dentry), DENTRY_PATH(new_dentry)); - smb_invalid_dir_cache(dir); - error = smb_proc_link(server_from_dentry(dentry), dentry, new_dentry); - if (!error) { - smb_renew_times(dentry); - error = smb_instantiate(new_dentry, 0, 0); - } - return error; -} diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c deleted file mode 100644 index 8e187a0f94bb..000000000000 --- a/fs/smbfs/file.c +++ /dev/null @@ -1,454 +0,0 @@ -/* - * file.c - * - * Copyright (C) 1995, 1996, 1997 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - * Please add a note about your changes to smbfs in the ChangeLog file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "smb_debug.h" -#include "proto.h" - -static int -smb_fsync(struct file *file, int datasync) -{ - struct dentry *dentry = file->f_path.dentry; - struct smb_sb_info *server = server_from_dentry(dentry); - int result; - - VERBOSE("sync file %s/%s\n", DENTRY_PATH(dentry)); - - /* - * The VFS will writepage() all dirty pages for us, but we - * should send a SMBflush to the server, letting it know that - * we want things synchronized with actual storage. - * - * Note: this function requires all pages to have been written already - * (should be ok with writepage_sync) - */ - result = smb_proc_flush(server, SMB_I(dentry->d_inode)->fileid); - return result; -} - -/* - * Read a page synchronously. - */ -static int -smb_readpage_sync(struct dentry *dentry, struct page *page) -{ - char *buffer = kmap(page); - loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT; - struct smb_sb_info *server = server_from_dentry(dentry); - unsigned int rsize = smb_get_rsize(server); - int count = PAGE_SIZE; - int result; - - VERBOSE("file %s/%s, count=%d@%Ld, rsize=%d\n", - DENTRY_PATH(dentry), count, offset, rsize); - - result = smb_open(dentry, SMB_O_RDONLY); - if (result < 0) - goto io_error; - - do { - if (count < rsize) - rsize = count; - - result = server->ops->read(dentry->d_inode,offset,rsize,buffer); - if (result < 0) - goto io_error; - - count -= result; - offset += result; - buffer += result; - dentry->d_inode->i_atime = - current_fs_time(dentry->d_inode->i_sb); - if (result < rsize) - break; - } while (count); - - memset(buffer, 0, count); - flush_dcache_page(page); - SetPageUptodate(page); - result = 0; - -io_error: - kunmap(page); - unlock_page(page); - return result; -} - -/* - * We are called with the page locked and we unlock it when done. - */ -static int -smb_readpage(struct file *file, struct page *page) -{ - int error; - struct dentry *dentry = file->f_path.dentry; - - page_cache_get(page); - error = smb_readpage_sync(dentry, page); - page_cache_release(page); - return error; -} - -/* - * Write a page synchronously. - * Offset is the data offset within the page. - */ -static int -smb_writepage_sync(struct inode *inode, struct page *page, - unsigned long pageoffset, unsigned int count) -{ - loff_t offset; - char *buffer = kmap(page) + pageoffset; - struct smb_sb_info *server = server_from_inode(inode); - unsigned int wsize = smb_get_wsize(server); - int ret = 0; - - offset = ((loff_t)page->index << PAGE_CACHE_SHIFT) + pageoffset; - VERBOSE("file ino=%ld, fileid=%d, count=%d@%Ld, wsize=%d\n", - inode->i_ino, SMB_I(inode)->fileid, count, offset, wsize); - - do { - int write_ret; - - if (count < wsize) - wsize = count; - - write_ret = server->ops->write(inode, offset, wsize, buffer); - if (write_ret < 0) { - PARANOIA("failed write, wsize=%d, write_ret=%d\n", - wsize, write_ret); - ret = write_ret; - break; - } - /* N.B. what if result < wsize?? */ -#ifdef SMBFS_PARANOIA - if (write_ret < wsize) - PARANOIA("short write, wsize=%d, write_ret=%d\n", - wsize, write_ret); -#endif - buffer += wsize; - offset += wsize; - count -= wsize; - /* - * Update the inode now rather than waiting for a refresh. - */ - inode->i_mtime = inode->i_atime = current_fs_time(inode->i_sb); - SMB_I(inode)->flags |= SMB_F_LOCALWRITE; - if (offset > inode->i_size) - inode->i_size = offset; - } while (count); - - kunmap(page); - return ret; -} - -/* - * Write a page to the server. This will be used for NFS swapping only - * (for now), and we currently do this synchronously only. - * - * We are called with the page locked and we unlock it when done. - */ -static int -smb_writepage(struct page *page, struct writeback_control *wbc) -{ - struct address_space *mapping = page->mapping; - struct inode *inode; - unsigned long end_index; - unsigned offset = PAGE_CACHE_SIZE; - int err; - - BUG_ON(!mapping); - inode = mapping->host; - BUG_ON(!inode); - - end_index = inode->i_size >> PAGE_CACHE_SHIFT; - - /* easy case */ - if (page->index < end_index) - goto do_it; - /* things got complicated... */ - offset = inode->i_size & (PAGE_CACHE_SIZE-1); - /* OK, are we completely out? */ - if (page->index >= end_index+1 || !offset) - return 0; /* truncated - don't care */ -do_it: - page_cache_get(page); - err = smb_writepage_sync(inode, page, 0, offset); - SetPageUptodate(page); - unlock_page(page); - page_cache_release(page); - return err; -} - -static int -smb_updatepage(struct file *file, struct page *page, unsigned long offset, - unsigned int count) -{ - struct dentry *dentry = file->f_path.dentry; - - DEBUG1("(%s/%s %d@%lld)\n", DENTRY_PATH(dentry), count, - ((unsigned long long)page->index << PAGE_CACHE_SHIFT) + offset); - - return smb_writepage_sync(dentry->d_inode, page, offset, count); -} - -static ssize_t -smb_file_aio_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - struct file * file = iocb->ki_filp; - struct dentry * dentry = file->f_path.dentry; - ssize_t status; - - VERBOSE("file %s/%s, count=%lu@%lu\n", DENTRY_PATH(dentry), - (unsigned long) iocb->ki_left, (unsigned long) pos); - - status = smb_revalidate_inode(dentry); - if (status) { - PARANOIA("%s/%s validation failed, error=%Zd\n", - DENTRY_PATH(dentry), status); - goto out; - } - - VERBOSE("before read, size=%ld, flags=%x, atime=%ld\n", - (long)dentry->d_inode->i_size, - dentry->d_inode->i_flags, dentry->d_inode->i_atime.tv_sec); - - status = generic_file_aio_read(iocb, iov, nr_segs, pos); -out: - return status; -} - -static int -smb_file_mmap(struct file * file, struct vm_area_struct * vma) -{ - struct dentry * dentry = file->f_path.dentry; - int status; - - VERBOSE("file %s/%s, address %lu - %lu\n", - DENTRY_PATH(dentry), vma->vm_start, vma->vm_end); - - status = smb_revalidate_inode(dentry); - if (status) { - PARANOIA("%s/%s validation failed, error=%d\n", - DENTRY_PATH(dentry), status); - goto out; - } - status = generic_file_mmap(file, vma); -out: - return status; -} - -static ssize_t -smb_file_splice_read(struct file *file, loff_t *ppos, - struct pipe_inode_info *pipe, size_t count, - unsigned int flags) -{ - struct dentry *dentry = file->f_path.dentry; - ssize_t status; - - VERBOSE("file %s/%s, pos=%Ld, count=%lu\n", - DENTRY_PATH(dentry), *ppos, count); - - status = smb_revalidate_inode(dentry); - if (status) { - PARANOIA("%s/%s validation failed, error=%Zd\n", - DENTRY_PATH(dentry), status); - goto out; - } - status = generic_file_splice_read(file, ppos, pipe, count, flags); -out: - return status; -} - -/* - * This does the "real" work of the write. The generic routine has - * allocated the page, locked it, done all the page alignment stuff - * calculations etc. Now we should just copy the data from user - * space and write it back to the real medium.. - * - * If the writer ends up delaying the write, the writer needs to - * increment the page use counts until he is done with the page. - */ -static int smb_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata) -{ - pgoff_t index = pos >> PAGE_CACHE_SHIFT; - *pagep = grab_cache_page_write_begin(mapping, index, flags); - if (!*pagep) - return -ENOMEM; - return 0; -} - -static int smb_write_end(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned copied, - struct page *page, void *fsdata) -{ - int status; - unsigned offset = pos & (PAGE_CACHE_SIZE - 1); - - lock_kernel(); - status = smb_updatepage(file, page, offset, copied); - unlock_kernel(); - - if (!status) { - if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE) - SetPageUptodate(page); - status = copied; - } - - unlock_page(page); - page_cache_release(page); - - return status; -} - -const struct address_space_operations smb_file_aops = { - .readpage = smb_readpage, - .writepage = smb_writepage, - .write_begin = smb_write_begin, - .write_end = smb_write_end, -}; - -/* - * Write to a file (through the page cache). - */ -static ssize_t -smb_file_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - struct file * file = iocb->ki_filp; - struct dentry * dentry = file->f_path.dentry; - ssize_t result; - - VERBOSE("file %s/%s, count=%lu@%lu\n", - DENTRY_PATH(dentry), - (unsigned long) iocb->ki_left, (unsigned long) pos); - - result = smb_revalidate_inode(dentry); - if (result) { - PARANOIA("%s/%s validation failed, error=%Zd\n", - DENTRY_PATH(dentry), result); - goto out; - } - - result = smb_open(dentry, SMB_O_WRONLY); - if (result) - goto out; - - if (iocb->ki_left > 0) { - result = generic_file_aio_write(iocb, iov, nr_segs, pos); - VERBOSE("pos=%ld, size=%ld, mtime=%ld, atime=%ld\n", - (long) file->f_pos, (long) dentry->d_inode->i_size, - dentry->d_inode->i_mtime.tv_sec, - dentry->d_inode->i_atime.tv_sec); - } -out: - return result; -} - -static int -smb_file_open(struct inode *inode, struct file * file) -{ - int result; - struct dentry *dentry = file->f_path.dentry; - int smb_mode = (file->f_mode & O_ACCMODE) - 1; - - lock_kernel(); - result = smb_open(dentry, smb_mode); - if (result) - goto out; - SMB_I(inode)->openers++; -out: - unlock_kernel(); - return result; -} - -static int -smb_file_release(struct inode *inode, struct file * file) -{ - lock_kernel(); - if (!--SMB_I(inode)->openers) { - /* We must flush any dirty pages now as we won't be able to - write anything after close. mmap can trigger this. - "openers" should perhaps include mmap'ers ... */ - filemap_write_and_wait(inode->i_mapping); - smb_close(inode); - } - unlock_kernel(); - return 0; -} - -/* - * Check whether the required access is compatible with - * an inode's permission. SMB doesn't recognize superuser - * privileges, so we need our own check for this. - */ -static int -smb_file_permission(struct inode *inode, int mask) -{ - int mode = inode->i_mode; - int error = 0; - - VERBOSE("mode=%x, mask=%x\n", mode, mask); - - /* Look at user permissions */ - mode >>= 6; - if (mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) - error = -EACCES; - return error; -} - -static loff_t smb_remote_llseek(struct file *file, loff_t offset, int origin) -{ - loff_t ret; - lock_kernel(); - ret = generic_file_llseek_unlocked(file, offset, origin); - unlock_kernel(); - return ret; -} - -const struct file_operations smb_file_operations = -{ - .llseek = smb_remote_llseek, - .read = do_sync_read, - .aio_read = smb_file_aio_read, - .write = do_sync_write, - .aio_write = smb_file_aio_write, - .unlocked_ioctl = smb_ioctl, - .mmap = smb_file_mmap, - .open = smb_file_open, - .release = smb_file_release, - .fsync = smb_fsync, - .splice_read = smb_file_splice_read, -}; - -const struct inode_operations smb_file_inode_operations = -{ - .permission = smb_file_permission, - .getattr = smb_getattr, - .setattr = smb_notify_change, -}; diff --git a/fs/smbfs/getopt.c b/fs/smbfs/getopt.c deleted file mode 100644 index 7ae0f5273ab1..000000000000 --- a/fs/smbfs/getopt.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * getopt.c - */ - -#include -#include -#include - -#include "getopt.h" - -/** - * smb_getopt - option parser - * @caller: name of the caller, for error messages - * @options: the options string - * @opts: an array of &struct option entries controlling parser operations - * @optopt: output; will contain the current option - * @optarg: output; will contain the value (if one exists) - * @flag: output; may be NULL; should point to a long for or'ing flags - * @value: output; may be NULL; will be overwritten with the integer value - * of the current argument. - * - * Helper to parse options on the format used by mount ("a=b,c=d,e,f"). - * Returns opts->val if a matching entry in the 'opts' array is found, - * 0 when no more tokens are found, -1 if an error is encountered. - */ -int smb_getopt(char *caller, char **options, struct option *opts, - char **optopt, char **optarg, unsigned long *flag, - unsigned long *value) -{ - char *token; - char *val; - int i; - - do { - if ((token = strsep(options, ",")) == NULL) - return 0; - } while (*token == '\0'); - *optopt = token; - - *optarg = NULL; - if ((val = strchr (token, '=')) != NULL) { - *val++ = 0; - if (value) - *value = simple_strtoul(val, NULL, 0); - *optarg = val; - } - - for (i = 0; opts[i].name != NULL; i++) { - if (!strcmp(opts[i].name, token)) { - if (!opts[i].flag && (!val || !*val)) { - printk("%s: the %s option requires an argument\n", - caller, token); - return -1; - } - - if (flag && opts[i].flag) - *flag |= opts[i].flag; - - return opts[i].val; - } - } - printk("%s: Unrecognized mount option %s\n", caller, token); - return -1; -} diff --git a/fs/smbfs/getopt.h b/fs/smbfs/getopt.h deleted file mode 100644 index 146219ac7c46..000000000000 --- a/fs/smbfs/getopt.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _LINUX_GETOPT_H -#define _LINUX_GETOPT_H - -struct option { - const char *name; - unsigned long flag; - int val; -}; - -extern int smb_getopt(char *caller, char **options, struct option *opts, - char **optopt, char **optarg, unsigned long *flag, - unsigned long *value); - -#endif /* _LINUX_GETOPT_H */ diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c deleted file mode 100644 index 450c91941988..000000000000 --- a/fs/smbfs/inode.c +++ /dev/null @@ -1,839 +0,0 @@ -/* - * inode.c - * - * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - * Please add a note about your changes to smbfs in the ChangeLog file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "smb_debug.h" -#include "getopt.h" -#include "proto.h" - -/* Always pick a default string */ -#ifdef CONFIG_SMB_NLS_REMOTE -#define SMB_NLS_REMOTE CONFIG_SMB_NLS_REMOTE -#else -#define SMB_NLS_REMOTE "" -#endif - -#define SMB_TTL_DEFAULT 1000 - -static void smb_evict_inode(struct inode *); -static void smb_put_super(struct super_block *); -static int smb_statfs(struct dentry *, struct kstatfs *); -static int smb_show_options(struct seq_file *, struct vfsmount *); - -static struct kmem_cache *smb_inode_cachep; - -static struct inode *smb_alloc_inode(struct super_block *sb) -{ - struct smb_inode_info *ei; - ei = (struct smb_inode_info *)kmem_cache_alloc(smb_inode_cachep, GFP_KERNEL); - if (!ei) - return NULL; - return &ei->vfs_inode; -} - -static void smb_destroy_inode(struct inode *inode) -{ - kmem_cache_free(smb_inode_cachep, SMB_I(inode)); -} - -static void init_once(void *foo) -{ - struct smb_inode_info *ei = (struct smb_inode_info *) foo; - - inode_init_once(&ei->vfs_inode); -} - -static int init_inodecache(void) -{ - smb_inode_cachep = kmem_cache_create("smb_inode_cache", - sizeof(struct smb_inode_info), - 0, (SLAB_RECLAIM_ACCOUNT| - SLAB_MEM_SPREAD), - init_once); - if (smb_inode_cachep == NULL) - return -ENOMEM; - return 0; -} - -static void destroy_inodecache(void) -{ - kmem_cache_destroy(smb_inode_cachep); -} - -static int smb_remount(struct super_block *sb, int *flags, char *data) -{ - *flags |= MS_NODIRATIME; - return 0; -} - -static const struct super_operations smb_sops = -{ - .alloc_inode = smb_alloc_inode, - .destroy_inode = smb_destroy_inode, - .drop_inode = generic_delete_inode, - .evict_inode = smb_evict_inode, - .put_super = smb_put_super, - .statfs = smb_statfs, - .show_options = smb_show_options, - .remount_fs = smb_remount, -}; - - -/* We are always generating a new inode here */ -struct inode * -smb_iget(struct super_block *sb, struct smb_fattr *fattr) -{ - struct smb_sb_info *server = SMB_SB(sb); - struct inode *result; - - DEBUG1("smb_iget: %p\n", fattr); - - result = new_inode(sb); - if (!result) - return result; - result->i_ino = fattr->f_ino; - SMB_I(result)->open = 0; - SMB_I(result)->fileid = 0; - SMB_I(result)->access = 0; - SMB_I(result)->flags = 0; - SMB_I(result)->closed = 0; - SMB_I(result)->openers = 0; - smb_set_inode_attr(result, fattr); - if (S_ISREG(result->i_mode)) { - result->i_op = &smb_file_inode_operations; - result->i_fop = &smb_file_operations; - result->i_data.a_ops = &smb_file_aops; - } else if (S_ISDIR(result->i_mode)) { - if (server->opt.capabilities & SMB_CAP_UNIX) - result->i_op = &smb_dir_inode_operations_unix; - else - result->i_op = &smb_dir_inode_operations; - result->i_fop = &smb_dir_operations; - } else if (S_ISLNK(result->i_mode)) { - result->i_op = &smb_link_inode_operations; - } else { - init_special_inode(result, result->i_mode, fattr->f_rdev); - } - insert_inode_hash(result); - return result; -} - -/* - * Copy the inode data to a smb_fattr structure. - */ -void -smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr) -{ - memset(fattr, 0, sizeof(struct smb_fattr)); - fattr->f_mode = inode->i_mode; - fattr->f_nlink = inode->i_nlink; - fattr->f_ino = inode->i_ino; - fattr->f_uid = inode->i_uid; - fattr->f_gid = inode->i_gid; - fattr->f_size = inode->i_size; - fattr->f_mtime = inode->i_mtime; - fattr->f_ctime = inode->i_ctime; - fattr->f_atime = inode->i_atime; - fattr->f_blocks = inode->i_blocks; - - fattr->attr = SMB_I(inode)->attr; - /* - * Keep the attributes in sync with the inode permissions. - */ - if (fattr->f_mode & S_IWUSR) - fattr->attr &= ~aRONLY; - else - fattr->attr |= aRONLY; -} - -/* - * Update the inode, possibly causing it to invalidate its pages if mtime/size - * is different from last time. - */ -void -smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr) -{ - struct smb_inode_info *ei = SMB_I(inode); - - /* - * A size change should have a different mtime, or same mtime - * but different size. - */ - time_t last_time = inode->i_mtime.tv_sec; - loff_t last_sz = inode->i_size; - - inode->i_mode = fattr->f_mode; - inode->i_nlink = fattr->f_nlink; - inode->i_uid = fattr->f_uid; - inode->i_gid = fattr->f_gid; - inode->i_ctime = fattr->f_ctime; - inode->i_blocks = fattr->f_blocks; - inode->i_size = fattr->f_size; - inode->i_mtime = fattr->f_mtime; - inode->i_atime = fattr->f_atime; - ei->attr = fattr->attr; - - /* - * Update the "last time refreshed" field for revalidation. - */ - ei->oldmtime = jiffies; - - if (inode->i_mtime.tv_sec != last_time || inode->i_size != last_sz) { - VERBOSE("%ld changed, old=%ld, new=%ld, oz=%ld, nz=%ld\n", - inode->i_ino, - (long) last_time, (long) inode->i_mtime.tv_sec, - (long) last_sz, (long) inode->i_size); - - if (!S_ISDIR(inode->i_mode)) - invalidate_remote_inode(inode); - } -} - -/* - * This is called if the connection has gone bad ... - * try to kill off all the current inodes. - */ -void -smb_invalidate_inodes(struct smb_sb_info *server) -{ - VERBOSE("\n"); - shrink_dcache_sb(SB_of(server)); - invalidate_inodes(SB_of(server)); -} - -/* - * This is called to update the inode attributes after - * we've made changes to a file or directory. - */ -static int -smb_refresh_inode(struct dentry *dentry) -{ - struct inode *inode = dentry->d_inode; - int error; - struct smb_fattr fattr; - - error = smb_proc_getattr(dentry, &fattr); - if (!error) { - smb_renew_times(dentry); - /* - * Check whether the type part of the mode changed, - * and don't update the attributes if it did. - * - * And don't dick with the root inode - */ - if (inode->i_ino == 2) - return error; - if (S_ISLNK(inode->i_mode)) - return error; /* VFS will deal with it */ - - if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) { - smb_set_inode_attr(inode, &fattr); - } else { - /* - * Big trouble! The inode has become a new object, - * so any operations attempted on it are invalid. - * - * To limit damage, mark the inode as bad so that - * subsequent lookup validations will fail. - */ - PARANOIA("%s/%s changed mode, %07o to %07o\n", - DENTRY_PATH(dentry), - inode->i_mode, fattr.f_mode); - - fattr.f_mode = inode->i_mode; /* save mode */ - make_bad_inode(inode); - inode->i_mode = fattr.f_mode; /* restore mode */ - /* - * No need to worry about unhashing the dentry: the - * lookup validation will see that the inode is bad. - * But we do want to invalidate the caches ... - */ - if (!S_ISDIR(inode->i_mode)) - invalidate_remote_inode(inode); - else - smb_invalid_dir_cache(inode); - error = -EIO; - } - } - return error; -} - -/* - * This is called when we want to check whether the inode - * has changed on the server. If it has changed, we must - * invalidate our local caches. - */ -int -smb_revalidate_inode(struct dentry *dentry) -{ - struct smb_sb_info *s = server_from_dentry(dentry); - struct inode *inode = dentry->d_inode; - int error = 0; - - DEBUG1("smb_revalidate_inode\n"); - lock_kernel(); - - /* - * Check whether we've recently refreshed the inode. - */ - if (time_before(jiffies, SMB_I(inode)->oldmtime + SMB_MAX_AGE(s))) { - VERBOSE("up-to-date, ino=%ld, jiffies=%lu, oldtime=%lu\n", - inode->i_ino, jiffies, SMB_I(inode)->oldmtime); - goto out; - } - - error = smb_refresh_inode(dentry); -out: - unlock_kernel(); - return error; -} - -/* - * This routine is called when i_nlink == 0 and i_count goes to 0. - * All blocking cleanup operations need to go here to avoid races. - */ -static void -smb_evict_inode(struct inode *ino) -{ - DEBUG1("ino=%ld\n", ino->i_ino); - truncate_inode_pages(&ino->i_data, 0); - end_writeback(ino); - lock_kernel(); - if (smb_close(ino)) - PARANOIA("could not close inode %ld\n", ino->i_ino); - unlock_kernel(); -} - -static struct option opts[] = { - { "version", 0, 'v' }, - { "win95", SMB_MOUNT_WIN95, 1 }, - { "oldattr", SMB_MOUNT_OLDATTR, 1 }, - { "dirattr", SMB_MOUNT_DIRATTR, 1 }, - { "case", SMB_MOUNT_CASE, 1 }, - { "uid", 0, 'u' }, - { "gid", 0, 'g' }, - { "file_mode", 0, 'f' }, - { "dir_mode", 0, 'd' }, - { "iocharset", 0, 'i' }, - { "codepage", 0, 'c' }, - { "ttl", 0, 't' }, - { NULL, 0, 0} -}; - -static int -parse_options(struct smb_mount_data_kernel *mnt, char *options) -{ - int c; - unsigned long flags; - unsigned long value; - char *optarg; - char *optopt; - - flags = 0; - while ( (c = smb_getopt("smbfs", &options, opts, - &optopt, &optarg, &flags, &value)) > 0) { - - VERBOSE("'%s' -> '%s'\n", optopt, optarg ? optarg : ""); - switch (c) { - case 1: - /* got a "flag" option */ - break; - case 'v': - if (value != SMB_MOUNT_VERSION) { - printk ("smbfs: Bad mount version %ld, expected %d\n", - value, SMB_MOUNT_VERSION); - return 0; - } - mnt->version = value; - break; - case 'u': - mnt->uid = value; - flags |= SMB_MOUNT_UID; - break; - case 'g': - mnt->gid = value; - flags |= SMB_MOUNT_GID; - break; - case 'f': - mnt->file_mode = (value & S_IRWXUGO) | S_IFREG; - flags |= SMB_MOUNT_FMODE; - break; - case 'd': - mnt->dir_mode = (value & S_IRWXUGO) | S_IFDIR; - flags |= SMB_MOUNT_DMODE; - break; - case 'i': - strlcpy(mnt->codepage.local_name, optarg, - SMB_NLS_MAXNAMELEN); - break; - case 'c': - strlcpy(mnt->codepage.remote_name, optarg, - SMB_NLS_MAXNAMELEN); - break; - case 't': - mnt->ttl = value; - break; - default: - printk ("smbfs: Unrecognized mount option %s\n", - optopt); - return -1; - } - } - mnt->flags = flags; - return c; -} - -/* - * smb_show_options() is for displaying mount options in /proc/mounts. - * It tries to avoid showing settings that were not changed from their - * defaults. - */ -static int -smb_show_options(struct seq_file *s, struct vfsmount *m) -{ - struct smb_mount_data_kernel *mnt = SMB_SB(m->mnt_sb)->mnt; - int i; - - for (i = 0; opts[i].name != NULL; i++) - if (mnt->flags & opts[i].flag) - seq_printf(s, ",%s", opts[i].name); - - if (mnt->flags & SMB_MOUNT_UID) - seq_printf(s, ",uid=%d", mnt->uid); - if (mnt->flags & SMB_MOUNT_GID) - seq_printf(s, ",gid=%d", mnt->gid); - if (mnt->mounted_uid != 0) - seq_printf(s, ",mounted_uid=%d", mnt->mounted_uid); - - /* - * Defaults for file_mode and dir_mode are unknown to us; they - * depend on the current umask of the user doing the mount. - */ - if (mnt->flags & SMB_MOUNT_FMODE) - seq_printf(s, ",file_mode=%04o", mnt->file_mode & S_IRWXUGO); - if (mnt->flags & SMB_MOUNT_DMODE) - seq_printf(s, ",dir_mode=%04o", mnt->dir_mode & S_IRWXUGO); - - if (strcmp(mnt->codepage.local_name, CONFIG_NLS_DEFAULT)) - seq_printf(s, ",iocharset=%s", mnt->codepage.local_name); - if (strcmp(mnt->codepage.remote_name, SMB_NLS_REMOTE)) - seq_printf(s, ",codepage=%s", mnt->codepage.remote_name); - - if (mnt->ttl != SMB_TTL_DEFAULT) - seq_printf(s, ",ttl=%d", mnt->ttl); - - return 0; -} - -static void -smb_unload_nls(struct smb_sb_info *server) -{ - unload_nls(server->remote_nls); - unload_nls(server->local_nls); -} - -static void -smb_put_super(struct super_block *sb) -{ - struct smb_sb_info *server = SMB_SB(sb); - - lock_kernel(); - - smb_lock_server(server); - server->state = CONN_INVALID; - smbiod_unregister_server(server); - - smb_close_socket(server); - - if (server->conn_pid) - kill_pid(server->conn_pid, SIGTERM, 1); - - bdi_destroy(&server->bdi); - kfree(server->ops); - smb_unload_nls(server); - sb->s_fs_info = NULL; - smb_unlock_server(server); - put_pid(server->conn_pid); - kfree(server); - - unlock_kernel(); -} - -static int smb_fill_super(struct super_block *sb, void *raw_data, int silent) -{ - struct smb_sb_info *server; - struct smb_mount_data_kernel *mnt; - struct smb_mount_data *oldmnt; - struct inode *root_inode; - struct smb_fattr root; - int ver; - void *mem; - static int warn_count; - - if (warn_count < 5) { - warn_count++; - printk(KERN_EMERG "smbfs is deprecated and will be removed" - " from the 2.6.27 kernel. Please migrate to cifs\n"); - } - - if (!raw_data) - goto out_no_data; - - oldmnt = (struct smb_mount_data *) raw_data; - ver = oldmnt->version; - if (ver != SMB_MOUNT_OLDVERSION && cpu_to_be32(ver) != SMB_MOUNT_ASCII) - goto out_wrong_data; - - sb->s_flags |= MS_NODIRATIME; - sb->s_blocksize = 1024; /* Eh... Is this correct? */ - sb->s_blocksize_bits = 10; - sb->s_magic = SMB_SUPER_MAGIC; - sb->s_op = &smb_sops; - sb->s_time_gran = 100; - - server = kzalloc(sizeof(struct smb_sb_info), GFP_KERNEL); - if (!server) - goto out_no_server; - sb->s_fs_info = server; - - if (bdi_setup_and_register(&server->bdi, "smbfs", BDI_CAP_MAP_COPY)) - goto out_bdi; - - sb->s_bdi = &server->bdi; - - server->super_block = sb; - server->mnt = NULL; - server->sock_file = NULL; - init_waitqueue_head(&server->conn_wq); - init_MUTEX(&server->sem); - INIT_LIST_HEAD(&server->entry); - INIT_LIST_HEAD(&server->xmitq); - INIT_LIST_HEAD(&server->recvq); - server->conn_error = 0; - server->conn_pid = NULL; - server->state = CONN_INVALID; /* no connection yet */ - server->generation = 0; - - /* Allocate the global temp buffer and some superblock helper structs */ - /* FIXME: move these to the smb_sb_info struct */ - VERBOSE("alloc chunk = %lu\n", sizeof(struct smb_ops) + - sizeof(struct smb_mount_data_kernel)); - mem = kmalloc(sizeof(struct smb_ops) + - sizeof(struct smb_mount_data_kernel), GFP_KERNEL); - if (!mem) - goto out_no_mem; - - server->ops = mem; - smb_install_null_ops(server->ops); - server->mnt = mem + sizeof(struct smb_ops); - - /* Setup NLS stuff */ - server->remote_nls = NULL; - server->local_nls = NULL; - - mnt = server->mnt; - - memset(mnt, 0, sizeof(struct smb_mount_data_kernel)); - strlcpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT, - SMB_NLS_MAXNAMELEN); - strlcpy(mnt->codepage.remote_name, SMB_NLS_REMOTE, - SMB_NLS_MAXNAMELEN); - - mnt->ttl = SMB_TTL_DEFAULT; - if (ver == SMB_MOUNT_OLDVERSION) { - mnt->version = oldmnt->version; - - SET_UID(mnt->uid, oldmnt->uid); - SET_GID(mnt->gid, oldmnt->gid); - - mnt->file_mode = (oldmnt->file_mode & S_IRWXUGO) | S_IFREG; - mnt->dir_mode = (oldmnt->dir_mode & S_IRWXUGO) | S_IFDIR; - - mnt->flags = (oldmnt->file_mode >> 9) | SMB_MOUNT_UID | - SMB_MOUNT_GID | SMB_MOUNT_FMODE | SMB_MOUNT_DMODE; - } else { - mnt->file_mode = S_IRWXU | S_IRGRP | S_IXGRP | - S_IROTH | S_IXOTH | S_IFREG; - mnt->dir_mode = S_IRWXU | S_IRGRP | S_IXGRP | - S_IROTH | S_IXOTH | S_IFDIR; - if (parse_options(mnt, raw_data)) - goto out_bad_option; - } - mnt->mounted_uid = current_uid(); - smb_setcodepage(server, &mnt->codepage); - - /* - * Display the enabled options - * Note: smb_proc_getattr uses these in 2.4 (but was changed in 2.2) - */ - if (mnt->flags & SMB_MOUNT_OLDATTR) - printk("SMBFS: Using core getattr (Win 95 speedup)\n"); - else if (mnt->flags & SMB_MOUNT_DIRATTR) - printk("SMBFS: Using dir ff getattr\n"); - - if (smbiod_register_server(server) < 0) { - printk(KERN_ERR "smbfs: failed to start smbiod\n"); - goto out_no_smbiod; - } - - /* - * Keep the super block locked while we get the root inode. - */ - smb_init_root_dirent(server, &root, sb); - root_inode = smb_iget(sb, &root); - if (!root_inode) - goto out_no_root; - - sb->s_root = d_alloc_root(root_inode); - if (!sb->s_root) - goto out_no_root; - - smb_new_dentry(sb->s_root); - - return 0; - -out_no_root: - iput(root_inode); -out_no_smbiod: - smb_unload_nls(server); -out_bad_option: - kfree(mem); -out_no_mem: - bdi_destroy(&server->bdi); -out_bdi: - if (!server->mnt) - printk(KERN_ERR "smb_fill_super: allocation failure\n"); - sb->s_fs_info = NULL; - kfree(server); - goto out_fail; -out_wrong_data: - printk(KERN_ERR "smbfs: mount_data version %d is not supported\n", ver); - goto out_fail; -out_no_data: - printk(KERN_ERR "smb_fill_super: missing data argument\n"); -out_fail: - return -EINVAL; -out_no_server: - printk(KERN_ERR "smb_fill_super: cannot allocate struct smb_sb_info\n"); - return -ENOMEM; -} - -static int -smb_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - int result; - - lock_kernel(); - - result = smb_proc_dskattr(dentry, buf); - - unlock_kernel(); - - buf->f_type = SMB_SUPER_MAGIC; - buf->f_namelen = SMB_MAXPATHLEN; - return result; -} - -int smb_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) -{ - int err = smb_revalidate_inode(dentry); - if (!err) - generic_fillattr(dentry->d_inode, stat); - return err; -} - -int -smb_notify_change(struct dentry *dentry, struct iattr *attr) -{ - struct inode *inode = dentry->d_inode; - struct smb_sb_info *server = server_from_dentry(dentry); - unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXUGO); - int error, changed, refresh = 0; - struct smb_fattr fattr; - - lock_kernel(); - - error = smb_revalidate_inode(dentry); - if (error) - goto out; - - if ((error = inode_change_ok(inode, attr)) < 0) - goto out; - - error = -EPERM; - if ((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->mnt->uid)) - goto out; - - if ((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->mnt->gid)) - goto out; - - if ((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~mask)) - goto out; - - if ((attr->ia_valid & ATTR_SIZE) != 0) { - VERBOSE("changing %s/%s, old size=%ld, new size=%ld\n", - DENTRY_PATH(dentry), - (long) inode->i_size, (long) attr->ia_size); - - filemap_write_and_wait(inode->i_mapping); - - error = smb_open(dentry, O_WRONLY); - if (error) - goto out; - error = server->ops->truncate(inode, attr->ia_size); - if (error) - goto out; - truncate_setsize(inode, attr->ia_size); - refresh = 1; - } - - if (server->opt.capabilities & SMB_CAP_UNIX) { - /* For now we don't want to set the size with setattr_unix */ - attr->ia_valid &= ~ATTR_SIZE; - /* FIXME: only call if we actually want to set something? */ - error = smb_proc_setattr_unix(dentry, attr, 0, 0); - if (!error) - refresh = 1; - - goto out; - } - - /* - * Initialize the fattr and check for changed fields. - * Note: CTIME under SMB is creation time rather than - * change time, so we don't attempt to change it. - */ - smb_get_inode_attr(inode, &fattr); - - changed = 0; - if ((attr->ia_valid & ATTR_MTIME) != 0) { - fattr.f_mtime = attr->ia_mtime; - changed = 1; - } - if ((attr->ia_valid & ATTR_ATIME) != 0) { - fattr.f_atime = attr->ia_atime; - /* Earlier protocols don't have an access time */ - if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) - changed = 1; - } - if (changed) { - error = smb_proc_settime(dentry, &fattr); - if (error) - goto out; - refresh = 1; - } - - /* - * Check for mode changes ... we're extremely limited in - * what can be set for SMB servers: just the read-only bit. - */ - if ((attr->ia_valid & ATTR_MODE) != 0) { - VERBOSE("%s/%s mode change, old=%x, new=%x\n", - DENTRY_PATH(dentry), fattr.f_mode, attr->ia_mode); - changed = 0; - if (attr->ia_mode & S_IWUSR) { - if (fattr.attr & aRONLY) { - fattr.attr &= ~aRONLY; - changed = 1; - } - } else { - if (!(fattr.attr & aRONLY)) { - fattr.attr |= aRONLY; - changed = 1; - } - } - if (changed) { - error = smb_proc_setattr(dentry, &fattr); - if (error) - goto out; - refresh = 1; - } - } - error = 0; - -out: - if (refresh) - smb_refresh_inode(dentry); - unlock_kernel(); - return error; -} - -static int smb_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, struct vfsmount *mnt) -{ - return get_sb_nodev(fs_type, flags, data, smb_fill_super, mnt); -} - -static struct file_system_type smb_fs_type = { - .owner = THIS_MODULE, - .name = "smbfs", - .get_sb = smb_get_sb, - .kill_sb = kill_anon_super, - .fs_flags = FS_BINARY_MOUNTDATA, -}; - -static int __init init_smb_fs(void) -{ - int err; - DEBUG1("registering ...\n"); - - err = init_inodecache(); - if (err) - goto out_inode; - err = smb_init_request_cache(); - if (err) - goto out_request; - err = register_filesystem(&smb_fs_type); - if (err) - goto out; - return 0; -out: - smb_destroy_request_cache(); -out_request: - destroy_inodecache(); -out_inode: - return err; -} - -static void __exit exit_smb_fs(void) -{ - DEBUG1("unregistering ...\n"); - unregister_filesystem(&smb_fs_type); - smb_destroy_request_cache(); - destroy_inodecache(); -} - -module_init(init_smb_fs) -module_exit(exit_smb_fs) -MODULE_LICENSE("GPL"); diff --git a/fs/smbfs/ioctl.c b/fs/smbfs/ioctl.c deleted file mode 100644 index 07215312ad39..000000000000 --- a/fs/smbfs/ioctl.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * ioctl.c - * - * Copyright (C) 1995, 1996 by Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - * Please add a note about your changes to smbfs in the ChangeLog file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "proto.h" - -long -smb_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct smb_sb_info *server = server_from_inode(filp->f_path.dentry->d_inode); - struct smb_conn_opt opt; - int result = -EINVAL; - - lock_kernel(); - switch (cmd) { - uid16_t uid16; - uid_t uid32; - case SMB_IOC_GETMOUNTUID: - SET_UID(uid16, server->mnt->mounted_uid); - result = put_user(uid16, (uid16_t __user *) arg); - break; - case SMB_IOC_GETMOUNTUID32: - SET_UID(uid32, server->mnt->mounted_uid); - result = put_user(uid32, (uid_t __user *) arg); - break; - - case SMB_IOC_NEWCONN: - /* arg is smb_conn_opt, or NULL if no connection was made */ - if (!arg) { - result = 0; - smb_lock_server(server); - server->state = CONN_RETRIED; - printk(KERN_ERR "Connection attempt failed! [%d]\n", - server->conn_error); - smbiod_flush(server); - smb_unlock_server(server); - break; - } - - result = -EFAULT; - if (!copy_from_user(&opt, (void __user *)arg, sizeof(opt))) - result = smb_newconn(server, &opt); - break; - default: - break; - } - unlock_kernel(); - - return result; -} diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c deleted file mode 100644 index 71c29b6670b4..000000000000 --- a/fs/smbfs/proc.c +++ /dev/null @@ -1,3507 +0,0 @@ -/* - * proc.c - * - * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - * Please add a note about your changes to smbfs in the ChangeLog file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "smb_debug.h" -#include "proto.h" -#include "request.h" - - -/* Features. Undefine if they cause problems, this should perhaps be a - config option. */ -#define SMBFS_POSIX_UNLINK 1 - -/* Allow smb_retry to be interrupted. */ -#define SMB_RETRY_INTR - -#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN) -#define SMB_CMD(packet) (*(packet+8)) -#define SMB_WCT(packet) (*(packet+SMB_HEADER_LEN - 1)) - -#define SMB_DIRINFO_SIZE 43 -#define SMB_STATUS_SIZE 21 - -#define SMB_ST_BLKSIZE (PAGE_SIZE) -#define SMB_ST_BLKSHIFT (PAGE_SHIFT) - -static struct smb_ops smb_ops_core; -static struct smb_ops smb_ops_os2; -static struct smb_ops smb_ops_win95; -static struct smb_ops smb_ops_winNT; -static struct smb_ops smb_ops_unix; -static struct smb_ops smb_ops_null; - -static void -smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); -static void -smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); -static int -smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr); -static int -smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, - struct smb_fattr *fattr); -static int -smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, - u16 attr); -static int -smb_proc_setattr_ext(struct smb_sb_info *server, - struct inode *inode, struct smb_fattr *fattr); -static int -smb_proc_query_cifsunix(struct smb_sb_info *server); -static void -install_ops(struct smb_ops *dst, struct smb_ops *src); - - -static void -str_upper(char *name, int len) -{ - while (len--) - { - if (*name >= 'a' && *name <= 'z') - *name -= ('a' - 'A'); - name++; - } -} - -#if 0 -static void -str_lower(char *name, int len) -{ - while (len--) - { - if (*name >= 'A' && *name <= 'Z') - *name += ('a' - 'A'); - name++; - } -} -#endif - -/* reverse a string inline. This is used by the dircache walking routines */ -static void reverse_string(char *buf, int len) -{ - char c; - char *end = buf+len-1; - - while(buf < end) { - c = *buf; - *(buf++) = *end; - *(end--) = c; - } -} - -/* no conversion, just a wrapper for memcpy. */ -static int convert_memcpy(unsigned char *output, int olen, - const unsigned char *input, int ilen, - struct nls_table *nls_from, - struct nls_table *nls_to) -{ - if (olen < ilen) - return -ENAMETOOLONG; - memcpy(output, input, ilen); - return ilen; -} - -static inline int write_char(unsigned char ch, char *output, int olen) -{ - if (olen < 4) - return -ENAMETOOLONG; - sprintf(output, ":x%02x", ch); - return 4; -} - -static inline int write_unichar(wchar_t ch, char *output, int olen) -{ - if (olen < 5) - return -ENAMETOOLONG; - sprintf(output, ":%04x", ch); - return 5; -} - -/* convert from one "codepage" to another (possibly being utf8). */ -static int convert_cp(unsigned char *output, int olen, - const unsigned char *input, int ilen, - struct nls_table *nls_from, - struct nls_table *nls_to) -{ - int len = 0; - int n; - wchar_t ch; - - while (ilen > 0) { - /* convert by changing to unicode and back to the new cp */ - n = nls_from->char2uni(input, ilen, &ch); - if (n == -EINVAL) { - ilen--; - n = write_char(*input++, output, olen); - if (n < 0) - goto fail; - output += n; - olen -= n; - len += n; - continue; - } else if (n < 0) - goto fail; - input += n; - ilen -= n; - - n = nls_to->uni2char(ch, output, olen); - if (n == -EINVAL) - n = write_unichar(ch, output, olen); - if (n < 0) - goto fail; - output += n; - olen -= n; - - len += n; - } - return len; -fail: - return n; -} - -/* ----------------------------------------------------------- */ - -/* - * nls_unicode - * - * This encodes/decodes little endian unicode format - */ - -static int uni2char(wchar_t uni, unsigned char *out, int boundlen) -{ - if (boundlen < 2) - return -EINVAL; - *out++ = uni & 0xff; - *out++ = uni >> 8; - return 2; -} - -static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni) -{ - if (boundlen < 2) - return -EINVAL; - *uni = (rawstring[1] << 8) | rawstring[0]; - return 2; -} - -static struct nls_table unicode_table = { - .charset = "unicode", - .uni2char = uni2char, - .char2uni = char2uni, -}; - -/* ----------------------------------------------------------- */ - -static int setcodepage(struct nls_table **p, char *name) -{ - struct nls_table *nls; - - if (!name || !*name) { - nls = NULL; - } else if ( (nls = load_nls(name)) == NULL) { - printk (KERN_ERR "smbfs: failed to load nls '%s'\n", name); - return -EINVAL; - } - - /* if already set, unload the previous one. */ - if (*p && *p != &unicode_table) - unload_nls(*p); - *p = nls; - - return 0; -} - -/* Handles all changes to codepage settings. */ -int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp) -{ - int n = 0; - - smb_lock_server(server); - - /* Don't load any nls_* at all, if no remote is requested */ - if (!*cp->remote_name) - goto out; - - /* local */ - n = setcodepage(&server->local_nls, cp->local_name); - if (n != 0) - goto out; - - /* remote */ - if (!strcmp(cp->remote_name, "unicode")) { - server->remote_nls = &unicode_table; - } else { - n = setcodepage(&server->remote_nls, cp->remote_name); - if (n != 0) - setcodepage(&server->local_nls, NULL); - } - -out: - if (server->local_nls != NULL && server->remote_nls != NULL) - server->ops->convert = convert_cp; - else - server->ops->convert = convert_memcpy; - - smb_unlock_server(server); - return n; -} - - -/*****************************************************************************/ -/* */ -/* Encoding/Decoding section */ -/* */ -/*****************************************************************************/ - -static __u8 * -smb_encode_smb_length(__u8 * p, __u32 len) -{ - *p = 0; - *(p+1) = 0; - *(p+2) = (len & 0xFF00) >> 8; - *(p+3) = (len & 0xFF); - if (len > 0xFFFF) - { - *(p+1) = 1; - } - return p + 4; -} - -/* - * smb_build_path: build the path to entry and name storing it in buf. - * The path returned will have the trailing '\0'. - */ -static int smb_build_path(struct smb_sb_info *server, unsigned char *buf, - int maxlen, - struct dentry *entry, struct qstr *name) -{ - unsigned char *path = buf; - int len; - int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE) != 0; - - if (maxlen < (2< SMB_MAXPATHLEN + 1) - maxlen = SMB_MAXPATHLEN + 1; - - if (entry == NULL) - goto test_name_and_out; - - /* - * If IS_ROOT, we have to do no walking at all. - */ - if (IS_ROOT(entry) && !name) { - *path++ = '\\'; - if (unicode) *path++ = '\0'; - *path++ = '\0'; - if (unicode) *path++ = '\0'; - return path-buf; - } - - /* - * Build the path string walking the tree backward from end to ROOT - * and store it in reversed order [see reverse_string()] - */ - dget(entry); - spin_lock(&entry->d_lock); - while (!IS_ROOT(entry)) { - struct dentry *parent; - - if (maxlen < (3<d_lock); - dput(entry); - return -ENAMETOOLONG; - } - - len = server->ops->convert(path, maxlen-2, - entry->d_name.name, entry->d_name.len, - server->local_nls, server->remote_nls); - if (len < 0) { - spin_unlock(&entry->d_lock); - dput(entry); - return len; - } - reverse_string(path, len); - path += len; - if (unicode) { - /* Note: reverse order */ - *path++ = '\0'; - maxlen--; - } - *path++ = '\\'; - maxlen -= len+1; - - parent = entry->d_parent; - dget(parent); - spin_unlock(&entry->d_lock); - dput(entry); - entry = parent; - spin_lock(&entry->d_lock); - } - spin_unlock(&entry->d_lock); - dput(entry); - reverse_string(buf, path-buf); - - /* maxlen has space for at least one char */ -test_name_and_out: - if (name) { - if (maxlen < (3<ops->convert(path, maxlen-2, - name->name, name->len, - server->local_nls, server->remote_nls); - if (len < 0) - return len; - path += len; - maxlen -= len+1; - } - /* maxlen has space for at least one char */ - *path++ = '\0'; - if (unicode) *path++ = '\0'; - return path-buf; -} - -static int smb_encode_path(struct smb_sb_info *server, char *buf, int maxlen, - struct dentry *dir, struct qstr *name) -{ - int result; - - result = smb_build_path(server, buf, maxlen, dir, name); - if (result < 0) - goto out; - if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS) - str_upper(buf, result); -out: - return result; -} - -/* encode_path for non-trans2 request SMBs */ -static int smb_simple_encode_path(struct smb_request *req, char **p, - struct dentry * entry, struct qstr * name) -{ - struct smb_sb_info *server = req->rq_server; - char *s = *p; - int res; - int maxlen = ((char *)req->rq_buffer + req->rq_bufsize) - s; - int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE); - - if (!maxlen) - return -ENAMETOOLONG; - *s++ = 4; /* ASCII data format */ - - /* - * SMB Unicode strings must be 16bit aligned relative the start of the - * packet. If they are not they must be padded with 0. - */ - if (unicode) { - int align = s - (char *)req->rq_buffer; - if (!(align & 1)) { - *s++ = '\0'; - maxlen--; - } - } - - res = smb_encode_path(server, s, maxlen-1, entry, name); - if (res < 0) - return res; - *p = s + res; - return 0; -} - -/* The following are taken directly from msdos-fs */ - -/* Linear day numbers of the respective 1sts in non-leap years. */ - -static int day_n[] = -{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0}; - /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ - - -static time_t -utc2local(struct smb_sb_info *server, time_t time) -{ - return time - server->opt.serverzone*60; -} - -static time_t -local2utc(struct smb_sb_info *server, time_t time) -{ - return time + server->opt.serverzone*60; -} - -/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ - -static time_t -date_dos2unix(struct smb_sb_info *server, __u16 date, __u16 time) -{ - int month, year; - time_t secs; - - /* first subtract and mask after that... Otherwise, if - date == 0, bad things happen */ - month = ((date >> 5) - 1) & 15; - year = date >> 9; - secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 * - ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 && - month < 2 ? 1 : 0) + 3653); - /* days since 1.1.70 plus 80's leap day */ - return local2utc(server, secs); -} - - -/* Convert linear UNIX date to a MS-DOS time/date pair. */ - -static void -date_unix2dos(struct smb_sb_info *server, - int unix_date, __u16 *date, __u16 *time) -{ - int day, year, nl_day, month; - - unix_date = utc2local(server, unix_date); - if (unix_date < 315532800) - unix_date = 315532800; - - *time = (unix_date % 60) / 2 + - (((unix_date / 60) % 60) << 5) + - (((unix_date / 3600) % 24) << 11); - - day = unix_date / 86400 - 3652; - year = day / 365; - if ((year + 3) / 4 + 365 * year > day) - year--; - day -= (year + 3) / 4 + 365 * year; - if (day == 59 && !(year & 3)) { - nl_day = day; - month = 2; - } else { - nl_day = (year & 3) || day <= 59 ? day : day - 1; - for (month = 1; month < 12; month++) - if (day_n[month] > nl_day) - break; - } - *date = nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9); -} - -/* The following are taken from fs/ntfs/util.c */ - -#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000) - -/* - * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) - * into Unix UTC (based 1970-01-01, in seconds). - */ -static struct timespec -smb_ntutc2unixutc(u64 ntutc) -{ - struct timespec ts; - /* FIXME: what about the timezone difference? */ - /* Subtract the NTFS time offset, then convert to 1s intervals. */ - u64 t = ntutc - NTFS_TIME_OFFSET; - ts.tv_nsec = do_div(t, 10000000) * 100; - ts.tv_sec = t; - return ts; -} - -/* Convert the Unix UTC into NT time */ -static u64 -smb_unixutc2ntutc(struct timespec ts) -{ - /* Note: timezone conversion is probably wrong. */ - /* return ((u64)utc2local(server, t)) * 10000000 + NTFS_TIME_OFFSET; */ - return ((u64)ts.tv_sec) * 10000000 + ts.tv_nsec/100 + NTFS_TIME_OFFSET; -} - -#define MAX_FILE_MODE 6 -static mode_t file_mode[] = { - S_IFREG, S_IFDIR, S_IFLNK, S_IFCHR, S_IFBLK, S_IFIFO, S_IFSOCK -}; - -static int smb_filetype_to_mode(u32 filetype) -{ - if (filetype > MAX_FILE_MODE) { - PARANOIA("Filetype out of range: %d\n", filetype); - return S_IFREG; - } - return file_mode[filetype]; -} - -static u32 smb_filetype_from_mode(int mode) -{ - if (S_ISREG(mode)) - return UNIX_TYPE_FILE; - if (S_ISDIR(mode)) - return UNIX_TYPE_DIR; - if (S_ISLNK(mode)) - return UNIX_TYPE_SYMLINK; - if (S_ISCHR(mode)) - return UNIX_TYPE_CHARDEV; - if (S_ISBLK(mode)) - return UNIX_TYPE_BLKDEV; - if (S_ISFIFO(mode)) - return UNIX_TYPE_FIFO; - if (S_ISSOCK(mode)) - return UNIX_TYPE_SOCKET; - return UNIX_TYPE_UNKNOWN; -} - - -/*****************************************************************************/ -/* */ -/* Support section. */ -/* */ -/*****************************************************************************/ - -__u32 -smb_len(__u8 * p) -{ - return ((*(p+1) & 0x1) << 16L) | (*(p+2) << 8L) | *(p+3); -} - -static __u16 -smb_bcc(__u8 * packet) -{ - int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(__u16); - return WVAL(packet, pos); -} - -/* smb_valid_packet: We check if packet fulfills the basic - requirements of a smb packet */ - -static int -smb_valid_packet(__u8 * packet) -{ - return (packet[4] == 0xff - && packet[5] == 'S' - && packet[6] == 'M' - && packet[7] == 'B' - && (smb_len(packet) + 4 == SMB_HEADER_LEN - + SMB_WCT(packet) * 2 + smb_bcc(packet))); -} - -/* smb_verify: We check if we got the answer we expected, and if we - got enough data. If bcc == -1, we don't care. */ - -static int -smb_verify(__u8 * packet, int command, int wct, int bcc) -{ - if (SMB_CMD(packet) != command) - goto bad_command; - if (SMB_WCT(packet) < wct) - goto bad_wct; - if (bcc != -1 && smb_bcc(packet) < bcc) - goto bad_bcc; - return 0; - -bad_command: - printk(KERN_ERR "smb_verify: command=%x, SMB_CMD=%x??\n", - command, SMB_CMD(packet)); - goto fail; -bad_wct: - printk(KERN_ERR "smb_verify: command=%x, wct=%d, SMB_WCT=%d??\n", - command, wct, SMB_WCT(packet)); - goto fail; -bad_bcc: - printk(KERN_ERR "smb_verify: command=%x, bcc=%d, SMB_BCC=%d??\n", - command, bcc, smb_bcc(packet)); -fail: - return -EIO; -} - -/* - * Returns the maximum read or write size for the "payload". Making all of the - * packet fit within the negotiated max_xmit size. - * - * N.B. Since this value is usually computed before locking the server, - * the server's packet size must never be decreased! - */ -static inline int -smb_get_xmitsize(struct smb_sb_info *server, int overhead) -{ - return server->opt.max_xmit - overhead; -} - -/* - * Calculate the maximum read size - */ -int -smb_get_rsize(struct smb_sb_info *server) -{ - /* readX has 12 parameters, read has 5 */ - int overhead = SMB_HEADER_LEN + 12 * sizeof(__u16) + 2 + 1 + 2; - int size = smb_get_xmitsize(server, overhead); - - VERBOSE("xmit=%d, size=%d\n", server->opt.max_xmit, size); - - return size; -} - -/* - * Calculate the maximum write size - */ -int -smb_get_wsize(struct smb_sb_info *server) -{ - /* writeX has 14 parameters, write has 5 */ - int overhead = SMB_HEADER_LEN + 14 * sizeof(__u16) + 2 + 1 + 2; - int size = smb_get_xmitsize(server, overhead); - - VERBOSE("xmit=%d, size=%d\n", server->opt.max_xmit, size); - - return size; -} - -/* - * Convert SMB error codes to -E... errno values. - */ -int -smb_errno(struct smb_request *req) -{ - int errcls = req->rq_rcls; - int error = req->rq_err; - char *class = "Unknown"; - - VERBOSE("errcls %d code %d from command 0x%x\n", - errcls, error, SMB_CMD(req->rq_header)); - - if (errcls == ERRDOS) { - switch (error) { - case ERRbadfunc: - return -EINVAL; - case ERRbadfile: - case ERRbadpath: - return -ENOENT; - case ERRnofids: - return -EMFILE; - case ERRnoaccess: - return -EACCES; - case ERRbadfid: - return -EBADF; - case ERRbadmcb: - return -EREMOTEIO; - case ERRnomem: - return -ENOMEM; - case ERRbadmem: - return -EFAULT; - case ERRbadenv: - case ERRbadformat: - return -EREMOTEIO; - case ERRbadaccess: - return -EACCES; - case ERRbaddata: - return -E2BIG; - case ERRbaddrive: - return -ENXIO; - case ERRremcd: - return -EREMOTEIO; - case ERRdiffdevice: - return -EXDEV; - case ERRnofiles: - return -ENOENT; - case ERRbadshare: - return -ETXTBSY; - case ERRlock: - return -EDEADLK; - case ERRfilexists: - return -EEXIST; - case ERROR_INVALID_PARAMETER: - return -EINVAL; - case ERROR_DISK_FULL: - return -ENOSPC; - case ERROR_INVALID_NAME: - return -ENOENT; - case ERROR_DIR_NOT_EMPTY: - return -ENOTEMPTY; - case ERROR_NOT_LOCKED: - return -ENOLCK; - case ERROR_ALREADY_EXISTS: - return -EEXIST; - default: - class = "ERRDOS"; - goto err_unknown; - } - } else if (errcls == ERRSRV) { - switch (error) { - /* N.B. This is wrong ... EIO ? */ - case ERRerror: - return -ENFILE; - case ERRbadpw: - return -EINVAL; - case ERRbadtype: - case ERRtimeout: - return -EIO; - case ERRaccess: - return -EACCES; - /* - * This is a fatal error, as it means the "tree ID" - * for this connection is no longer valid. We map - * to a special error code and get a new connection. - */ - case ERRinvnid: - return -EBADSLT; - default: - class = "ERRSRV"; - goto err_unknown; - } - } else if (errcls == ERRHRD) { - switch (error) { - case ERRnowrite: - return -EROFS; - case ERRbadunit: - return -ENODEV; - case ERRnotready: - return -EUCLEAN; - case ERRbadcmd: - case ERRdata: - return -EIO; - case ERRbadreq: - return -ERANGE; - case ERRbadshare: - return -ETXTBSY; - case ERRlock: - return -EDEADLK; - case ERRdiskfull: - return -ENOSPC; - default: - class = "ERRHRD"; - goto err_unknown; - } - } else if (errcls == ERRCMD) { - class = "ERRCMD"; - } else if (errcls == SUCCESS) { - return 0; /* This is the only valid 0 return */ - } - -err_unknown: - printk(KERN_ERR "smb_errno: class %s, code %d from command 0x%x\n", - class, error, SMB_CMD(req->rq_header)); - return -EIO; -} - -/* smb_request_ok: We expect the server to be locked. Then we do the - request and check the answer completely. When smb_request_ok - returns 0, you can be quite sure that everything went well. When - the answer is <=0, the returned number is a valid unix errno. */ - -static int -smb_request_ok(struct smb_request *req, int command, int wct, int bcc) -{ - int result; - - req->rq_resp_wct = wct; - req->rq_resp_bcc = bcc; - - result = smb_add_request(req); - if (result != 0) { - DEBUG1("smb_request failed\n"); - goto out; - } - - if (smb_valid_packet(req->rq_header) != 0) { - PARANOIA("invalid packet!\n"); - goto out; - } - - result = smb_verify(req->rq_header, command, wct, bcc); - -out: - return result; -} - -/* - * This implements the NEWCONN ioctl. It installs the server pid, - * sets server->state to CONN_VALID, and wakes up the waiting process. - */ -int -smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt) -{ - struct file *filp; - struct sock *sk; - int error; - - VERBOSE("fd=%d, pid=%d\n", opt->fd, current->pid); - - smb_lock_server(server); - - /* - * Make sure we don't already have a valid connection ... - */ - error = -EINVAL; - if (server->state == CONN_VALID) - goto out; - - error = -EACCES; - if (current_uid() != server->mnt->mounted_uid && - !capable(CAP_SYS_ADMIN)) - goto out; - - error = -EBADF; - filp = fget(opt->fd); - if (!filp) - goto out; - if (!smb_valid_socket(filp->f_path.dentry->d_inode)) - goto out_putf; - - server->sock_file = filp; - server->conn_pid = get_pid(task_pid(current)); - server->opt = *opt; - server->generation += 1; - server->state = CONN_VALID; - error = 0; - - if (server->conn_error) { - /* - * conn_error is the returncode we originally decided to - * drop the old connection on. This message should be positive - * and not make people ask questions on why smbfs is printing - * error messages ... - */ - printk(KERN_INFO "SMB connection re-established (%d)\n", - server->conn_error); - server->conn_error = 0; - } - - /* - * Store the server in sock user_data (Only used by sunrpc) - */ - sk = SOCKET_I(filp->f_path.dentry->d_inode)->sk; - sk->sk_user_data = server; - - /* chain into the data_ready callback */ - server->data_ready = xchg(&sk->sk_data_ready, smb_data_ready); - - /* check if we have an old smbmount that uses seconds for the - serverzone */ - if (server->opt.serverzone > 12*60 || server->opt.serverzone < -12*60) - server->opt.serverzone /= 60; - - /* now that we have an established connection we can detect the server - type and enable bug workarounds */ - if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) - install_ops(server->ops, &smb_ops_core); - else if (server->opt.protocol == SMB_PROTOCOL_LANMAN2) - install_ops(server->ops, &smb_ops_os2); - else if (server->opt.protocol == SMB_PROTOCOL_NT1 && - (server->opt.max_xmit < 0x1000) && - !(server->opt.capabilities & SMB_CAP_NT_SMBS)) { - /* FIXME: can we kill the WIN95 flag now? */ - server->mnt->flags |= SMB_MOUNT_WIN95; - VERBOSE("detected WIN95 server\n"); - install_ops(server->ops, &smb_ops_win95); - } else { - /* - * Samba has max_xmit 65535 - * NT4spX has max_xmit 4536 (or something like that) - * win2k has ... - */ - VERBOSE("detected NT1 (Samba, NT4/5) server\n"); - install_ops(server->ops, &smb_ops_winNT); - } - - /* FIXME: the win9x code wants to modify these ... (seek/trunc bug) */ - if (server->mnt->flags & SMB_MOUNT_OLDATTR) { - server->ops->getattr = smb_proc_getattr_core; - } else if (server->mnt->flags & SMB_MOUNT_DIRATTR) { - server->ops->getattr = smb_proc_getattr_ff; - } - - /* Decode server capabilities */ - if (server->opt.capabilities & SMB_CAP_LARGE_FILES) { - /* Should be ok to set this now, as no one can access the - mount until the connection has been established. */ - SB_of(server)->s_maxbytes = ~0ULL >> 1; - VERBOSE("LFS enabled\n"); - } - if (server->opt.capabilities & SMB_CAP_UNICODE) { - server->mnt->flags |= SMB_MOUNT_UNICODE; - VERBOSE("Unicode enabled\n"); - } else { - server->mnt->flags &= ~SMB_MOUNT_UNICODE; - } -#if 0 - /* flags we may test for other patches ... */ - if (server->opt.capabilities & SMB_CAP_LARGE_READX) { - VERBOSE("Large reads enabled\n"); - } - if (server->opt.capabilities & SMB_CAP_LARGE_WRITEX) { - VERBOSE("Large writes enabled\n"); - } -#endif - if (server->opt.capabilities & SMB_CAP_UNIX) { - struct inode *inode; - VERBOSE("Using UNIX CIFS extensions\n"); - install_ops(server->ops, &smb_ops_unix); - inode = SB_of(server)->s_root->d_inode; - if (inode) - inode->i_op = &smb_dir_inode_operations_unix; - } - - VERBOSE("protocol=%d, max_xmit=%d, pid=%d capabilities=0x%x\n", - server->opt.protocol, server->opt.max_xmit, - pid_nr(server->conn_pid), server->opt.capabilities); - - /* FIXME: this really should be done by smbmount. */ - if (server->opt.max_xmit > SMB_MAX_PACKET_SIZE) { - server->opt.max_xmit = SMB_MAX_PACKET_SIZE; - } - - smb_unlock_server(server); - smbiod_wake_up(); - if (server->opt.capabilities & SMB_CAP_UNIX) - smb_proc_query_cifsunix(server); - - server->conn_complete++; - wake_up_interruptible_all(&server->conn_wq); - return error; - -out: - smb_unlock_server(server); - smbiod_wake_up(); - return error; - -out_putf: - fput(filp); - goto out; -} - -/* smb_setup_header: We completely set up the packet. You only have to - insert the command-specific fields */ - -__u8 * -smb_setup_header(struct smb_request *req, __u8 command, __u16 wct, __u16 bcc) -{ - __u32 xmit_len = SMB_HEADER_LEN + wct * sizeof(__u16) + bcc + 2; - __u8 *p = req->rq_header; - struct smb_sb_info *server = req->rq_server; - - p = smb_encode_smb_length(p, xmit_len - 4); - - *p++ = 0xff; - *p++ = 'S'; - *p++ = 'M'; - *p++ = 'B'; - *p++ = command; - - memset(p, '\0', 19); - p += 19; - p += 8; - - if (server->opt.protocol > SMB_PROTOCOL_CORE) { - int flags = SMB_FLAGS_CASELESS_PATHNAMES; - int flags2 = SMB_FLAGS2_LONG_PATH_COMPONENTS | - SMB_FLAGS2_EXTENDED_ATTRIBUTES; /* EA? not really ... */ - - *(req->rq_header + smb_flg) = flags; - if (server->mnt->flags & SMB_MOUNT_UNICODE) - flags2 |= SMB_FLAGS2_UNICODE_STRINGS; - WSET(req->rq_header, smb_flg2, flags2); - } - *p++ = wct; /* wct */ - p += 2 * wct; - WSET(p, 0, bcc); - - /* Include the header in the data to send */ - req->rq_iovlen = 1; - req->rq_iov[0].iov_base = req->rq_header; - req->rq_iov[0].iov_len = xmit_len - bcc; - - return req->rq_buffer; -} - -static void -smb_setup_bcc(struct smb_request *req, __u8 *p) -{ - u16 bcc = p - req->rq_buffer; - u8 *pbcc = req->rq_header + SMB_HEADER_LEN + 2*SMB_WCT(req->rq_header); - - WSET(pbcc, 0, bcc); - - smb_encode_smb_length(req->rq_header, SMB_HEADER_LEN + - 2*SMB_WCT(req->rq_header) - 2 + bcc); - - /* Include the "bytes" in the data to send */ - req->rq_iovlen = 2; - req->rq_iov[1].iov_base = req->rq_buffer; - req->rq_iov[1].iov_len = bcc; -} - -static int -smb_proc_seek(struct smb_sb_info *server, __u16 fileid, - __u16 mode, off_t offset) -{ - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - smb_setup_header(req, SMBlseek, 4, 0); - WSET(req->rq_header, smb_vwv0, fileid); - WSET(req->rq_header, smb_vwv1, mode); - DSET(req->rq_header, smb_vwv2, offset); - req->rq_flags |= SMB_REQ_NORETRY; - - result = smb_request_ok(req, SMBlseek, 2, 0); - if (result < 0) { - result = 0; - goto out_free; - } - - result = DVAL(req->rq_header, smb_vwv0); -out_free: - smb_rput(req); -out: - return result; -} - -static int -smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish) -{ - struct inode *ino = dentry->d_inode; - struct smb_inode_info *ei = SMB_I(ino); - int mode, read_write = 0x42, read_only = 0x40; - int res; - char *p; - struct smb_request *req; - - /* - * Attempt to open r/w, unless there are no write privileges. - */ - mode = read_write; - if (!(ino->i_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) - mode = read_only; -#if 0 - /* FIXME: why is this code not in? below we fix it so that a caller - wanting RO doesn't get RW. smb_revalidate_inode does some - optimization based on access mode. tail -f needs it to be correct. - - We must open rw since we don't do the open if called a second time - with different 'wish'. Is that not supported by smb servers? */ - if (!(wish & (O_WRONLY | O_RDWR))) - mode = read_only; -#endif - - res = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - retry: - p = smb_setup_header(req, SMBopen, 2, 0); - WSET(req->rq_header, smb_vwv0, mode); - WSET(req->rq_header, smb_vwv1, aSYSTEM | aHIDDEN | aDIR); - res = smb_simple_encode_path(req, &p, dentry, NULL); - if (res < 0) - goto out_free; - smb_setup_bcc(req, p); - - res = smb_request_ok(req, SMBopen, 7, 0); - if (res != 0) { - if (mode == read_write && - (res == -EACCES || res == -ETXTBSY || res == -EROFS)) - { - VERBOSE("%s/%s R/W failed, error=%d, retrying R/O\n", - DENTRY_PATH(dentry), res); - mode = read_only; - req->rq_flags = 0; - goto retry; - } - goto out_free; - } - /* We should now have data in vwv[0..6]. */ - - ei->fileid = WVAL(req->rq_header, smb_vwv0); - ei->attr = WVAL(req->rq_header, smb_vwv1); - /* smb_vwv2 has mtime */ - /* smb_vwv4 has size */ - ei->access = (WVAL(req->rq_header, smb_vwv6) & SMB_ACCMASK); - ei->open = server->generation; - -out_free: - smb_rput(req); -out: - return res; -} - -/* - * Make sure the file is open, and check that the access - * is compatible with the desired access. - */ -int -smb_open(struct dentry *dentry, int wish) -{ - struct inode *inode = dentry->d_inode; - int result; - __u16 access; - - result = -ENOENT; - if (!inode) { - printk(KERN_ERR "smb_open: no inode for dentry %s/%s\n", - DENTRY_PATH(dentry)); - goto out; - } - - if (!smb_is_open(inode)) { - struct smb_sb_info *server = server_from_inode(inode); - result = 0; - if (!smb_is_open(inode)) - result = smb_proc_open(server, dentry, wish); - if (result) - goto out; - /* - * A successful open means the path is still valid ... - */ - smb_renew_times(dentry); - } - - /* - * Check whether the access is compatible with the desired mode. - */ - result = 0; - access = SMB_I(inode)->access; - if (access != wish && access != SMB_O_RDWR) { - PARANOIA("%s/%s access denied, access=%x, wish=%x\n", - DENTRY_PATH(dentry), access, wish); - result = -EACCES; - } -out: - return result; -} - -static int -smb_proc_close(struct smb_sb_info *server, __u16 fileid, __u32 mtime) -{ - struct smb_request *req; - int result = -ENOMEM; - - if (! (req = smb_alloc_request(server, 0))) - goto out; - - smb_setup_header(req, SMBclose, 3, 0); - WSET(req->rq_header, smb_vwv0, fileid); - DSET(req->rq_header, smb_vwv1, utc2local(server, mtime)); - req->rq_flags |= SMB_REQ_NORETRY; - result = smb_request_ok(req, SMBclose, 0, 0); - - smb_rput(req); -out: - return result; -} - -/* - * Win NT 4.0 has an apparent bug in that it fails to update the - * modify time when writing to a file. As a workaround, we update - * both modify and access time locally, and post the times to the - * server when closing the file. - */ -static int -smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino) -{ - struct smb_inode_info *ei = SMB_I(ino); - int result = 0; - if (smb_is_open(ino)) - { - /* - * We clear the open flag in advance, in case another - * process observes the value while we block below. - */ - ei->open = 0; - - /* - * Kludge alert: SMB timestamps are accurate only to - * two seconds ... round the times to avoid needless - * cache invalidations! - */ - if (ino->i_mtime.tv_sec & 1) { - ino->i_mtime.tv_sec--; - ino->i_mtime.tv_nsec = 0; - } - if (ino->i_atime.tv_sec & 1) { - ino->i_atime.tv_sec--; - ino->i_atime.tv_nsec = 0; - } - /* - * If the file is open with write permissions, - * update the time stamps to sync mtime and atime. - */ - if ((server->opt.capabilities & SMB_CAP_UNIX) == 0 && - (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) && - !(ei->access == SMB_O_RDONLY)) - { - struct smb_fattr fattr; - smb_get_inode_attr(ino, &fattr); - smb_proc_setattr_ext(server, ino, &fattr); - } - - result = smb_proc_close(server, ei->fileid, ino->i_mtime.tv_sec); - /* - * Force a revalidation after closing ... some servers - * don't post the size until the file has been closed. - */ - if (server->opt.protocol < SMB_PROTOCOL_NT1) - ei->oldmtime = 0; - ei->closed = jiffies; - } - return result; -} - -int -smb_close(struct inode *ino) -{ - int result = 0; - - if (smb_is_open(ino)) { - struct smb_sb_info *server = server_from_inode(ino); - result = smb_proc_close_inode(server, ino); - } - return result; -} - -/* - * This is used to close a file following a failed instantiate. - * Since we don't have an inode, we can't use any of the above. - */ -int -smb_close_fileid(struct dentry *dentry, __u16 fileid) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - int result; - - result = smb_proc_close(server, fileid, get_seconds()); - return result; -} - -/* In smb_proc_read and smb_proc_write we do not retry, because the - file-id would not be valid after a reconnection. */ - -static void -smb_proc_read_data(struct smb_request *req) -{ - req->rq_iov[0].iov_base = req->rq_buffer; - req->rq_iov[0].iov_len = 3; - - req->rq_iov[1].iov_base = req->rq_page; - req->rq_iov[1].iov_len = req->rq_rsize; - req->rq_iovlen = 2; - - req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd; -} - -static int -smb_proc_read(struct inode *inode, loff_t offset, int count, char *data) -{ - struct smb_sb_info *server = server_from_inode(inode); - __u16 returned_count, data_len; - unsigned char *buf; - int result; - struct smb_request *req; - u8 rbuf[4]; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - smb_setup_header(req, SMBread, 5, 0); - buf = req->rq_header; - WSET(buf, smb_vwv0, SMB_I(inode)->fileid); - WSET(buf, smb_vwv1, count); - DSET(buf, smb_vwv2, offset); - WSET(buf, smb_vwv4, 0); - - req->rq_page = data; - req->rq_rsize = count; - req->rq_callback = smb_proc_read_data; - req->rq_buffer = rbuf; - req->rq_flags |= SMB_REQ_NORETRY | SMB_REQ_STATIC; - - result = smb_request_ok(req, SMBread, 5, -1); - if (result < 0) - goto out_free; - returned_count = WVAL(req->rq_header, smb_vwv0); - - data_len = WVAL(rbuf, 1); - - if (returned_count != data_len) { - printk(KERN_NOTICE "smb_proc_read: returned != data_len\n"); - printk(KERN_NOTICE "smb_proc_read: ret_c=%d, data_len=%d\n", - returned_count, data_len); - } - result = data_len; - -out_free: - smb_rput(req); -out: - VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n", - inode->i_ino, SMB_I(inode)->fileid, count, result); - return result; -} - -static int -smb_proc_write(struct inode *inode, loff_t offset, int count, const char *data) -{ - struct smb_sb_info *server = server_from_inode(inode); - int result; - u16 fileid = SMB_I(inode)->fileid; - u8 buf[4]; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld\n", - inode->i_ino, fileid, count, offset); - - smb_setup_header(req, SMBwrite, 5, count + 3); - WSET(req->rq_header, smb_vwv0, fileid); - WSET(req->rq_header, smb_vwv1, count); - DSET(req->rq_header, smb_vwv2, offset); - WSET(req->rq_header, smb_vwv4, 0); - - buf[0] = 1; - WSET(buf, 1, count); /* yes, again ... */ - req->rq_iov[1].iov_base = buf; - req->rq_iov[1].iov_len = 3; - req->rq_iov[2].iov_base = (char *) data; - req->rq_iov[2].iov_len = count; - req->rq_iovlen = 3; - req->rq_flags |= SMB_REQ_NORETRY; - - result = smb_request_ok(req, SMBwrite, 1, 0); - if (result >= 0) - result = WVAL(req->rq_header, smb_vwv0); - - smb_rput(req); -out: - return result; -} - -/* - * In smb_proc_readX and smb_proc_writeX we do not retry, because the - * file-id would not be valid after a reconnection. - */ - -#define SMB_READX_MAX_PAD 64 -static void -smb_proc_readX_data(struct smb_request *req) -{ - /* header length, excluding the netbios length (-4) */ - int hdrlen = SMB_HEADER_LEN + req->rq_resp_wct*2 - 2; - int data_off = WVAL(req->rq_header, smb_vwv6); - - /* - * Some genius made the padding to the data bytes arbitrary. - * So we must first calculate the amount of padding used by the server. - */ - data_off -= hdrlen; - if (data_off > SMB_READX_MAX_PAD || data_off < 0) { - PARANOIA("offset is larger than SMB_READX_MAX_PAD or negative!\n"); - PARANOIA("%d > %d || %d < 0\n", data_off, SMB_READX_MAX_PAD, data_off); - req->rq_rlen = req->rq_bufsize + 1; - return; - } - req->rq_iov[0].iov_base = req->rq_buffer; - req->rq_iov[0].iov_len = data_off; - - req->rq_iov[1].iov_base = req->rq_page; - req->rq_iov[1].iov_len = req->rq_rsize; - req->rq_iovlen = 2; - - req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd; -} - -static int -smb_proc_readX(struct inode *inode, loff_t offset, int count, char *data) -{ - struct smb_sb_info *server = server_from_inode(inode); - unsigned char *buf; - int result; - struct smb_request *req; - static char pad[SMB_READX_MAX_PAD]; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - smb_setup_header(req, SMBreadX, 12, 0); - buf = req->rq_header; - WSET(buf, smb_vwv0, 0x00ff); - WSET(buf, smb_vwv1, 0); - WSET(buf, smb_vwv2, SMB_I(inode)->fileid); - DSET(buf, smb_vwv3, (u32)offset); /* low 32 bits */ - WSET(buf, smb_vwv5, count); - WSET(buf, smb_vwv6, 0); - DSET(buf, smb_vwv7, 0); - WSET(buf, smb_vwv9, 0); - DSET(buf, smb_vwv10, (u32)(offset >> 32)); /* high 32 bits */ - WSET(buf, smb_vwv11, 0); - - req->rq_page = data; - req->rq_rsize = count; - req->rq_callback = smb_proc_readX_data; - req->rq_buffer = pad; - req->rq_bufsize = SMB_READX_MAX_PAD; - req->rq_flags |= SMB_REQ_STATIC | SMB_REQ_NORETRY; - - result = smb_request_ok(req, SMBreadX, 12, -1); - if (result < 0) - goto out_free; - result = WVAL(req->rq_header, smb_vwv5); - -out_free: - smb_rput(req); -out: - VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n", - inode->i_ino, SMB_I(inode)->fileid, count, result); - return result; -} - -static int -smb_proc_writeX(struct inode *inode, loff_t offset, int count, const char *data) -{ - struct smb_sb_info *server = server_from_inode(inode); - int result; - u8 *p; - static u8 pad[4]; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld\n", - inode->i_ino, SMB_I(inode)->fileid, count, offset); - - p = smb_setup_header(req, SMBwriteX, 14, count + 1); - WSET(req->rq_header, smb_vwv0, 0x00ff); - WSET(req->rq_header, smb_vwv1, 0); - WSET(req->rq_header, smb_vwv2, SMB_I(inode)->fileid); - DSET(req->rq_header, smb_vwv3, (u32)offset); /* low 32 bits */ - DSET(req->rq_header, smb_vwv5, 0); - WSET(req->rq_header, smb_vwv7, 0); /* write mode */ - WSET(req->rq_header, smb_vwv8, 0); - WSET(req->rq_header, smb_vwv9, 0); - WSET(req->rq_header, smb_vwv10, count); /* data length */ - WSET(req->rq_header, smb_vwv11, smb_vwv12 + 2 + 1); - DSET(req->rq_header, smb_vwv12, (u32)(offset >> 32)); - - req->rq_iov[1].iov_base = pad; - req->rq_iov[1].iov_len = 1; - req->rq_iov[2].iov_base = (char *) data; - req->rq_iov[2].iov_len = count; - req->rq_iovlen = 3; - req->rq_flags |= SMB_REQ_NORETRY; - - result = smb_request_ok(req, SMBwriteX, 6, 0); - if (result >= 0) - result = WVAL(req->rq_header, smb_vwv2); - - smb_rput(req); -out: - return result; -} - -int -smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - char *p; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - p = smb_setup_header(req, SMBcreate, 3, 0); - WSET(req->rq_header, smb_vwv0, attr); - DSET(req->rq_header, smb_vwv1, utc2local(server, ctime)); - result = smb_simple_encode_path(req, &p, dentry, NULL); - if (result < 0) - goto out_free; - smb_setup_bcc(req, p); - - result = smb_request_ok(req, SMBcreate, 1, 0); - if (result < 0) - goto out_free; - - *fileid = WVAL(req->rq_header, smb_vwv0); - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -int -smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry) -{ - struct smb_sb_info *server = server_from_dentry(old_dentry); - char *p; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - p = smb_setup_header(req, SMBmv, 1, 0); - WSET(req->rq_header, smb_vwv0, aSYSTEM | aHIDDEN | aDIR); - result = smb_simple_encode_path(req, &p, old_dentry, NULL); - if (result < 0) - goto out_free; - result = smb_simple_encode_path(req, &p, new_dentry, NULL); - if (result < 0) - goto out_free; - smb_setup_bcc(req, p); - - if ((result = smb_request_ok(req, SMBmv, 0, 0)) < 0) - goto out_free; - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -/* - * Code common to mkdir and rmdir. - */ -static int -smb_proc_generic_command(struct dentry *dentry, __u8 command) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - char *p; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - p = smb_setup_header(req, command, 0, 0); - result = smb_simple_encode_path(req, &p, dentry, NULL); - if (result < 0) - goto out_free; - smb_setup_bcc(req, p); - - result = smb_request_ok(req, command, 0, 0); - if (result < 0) - goto out_free; - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -int -smb_proc_mkdir(struct dentry *dentry) -{ - return smb_proc_generic_command(dentry, SMBmkdir); -} - -int -smb_proc_rmdir(struct dentry *dentry) -{ - return smb_proc_generic_command(dentry, SMBrmdir); -} - -#if SMBFS_POSIX_UNLINK -/* - * Removes readonly attribute from a file. Used by unlink to give posix - * semantics. - */ -static int -smb_set_rw(struct dentry *dentry,struct smb_sb_info *server) -{ - int result; - struct smb_fattr fattr; - - /* FIXME: cifsUE should allow removing a readonly file. */ - - /* first get current attribute */ - smb_init_dirent(server, &fattr); - result = server->ops->getattr(server, dentry, &fattr); - smb_finish_dirent(server, &fattr); - if (result < 0) - return result; - - /* if RONLY attribute is set, remove it */ - if (fattr.attr & aRONLY) { /* read only attribute is set */ - fattr.attr &= ~aRONLY; - result = smb_proc_setattr_core(server, dentry, fattr.attr); - } - return result; -} -#endif - -int -smb_proc_unlink(struct dentry *dentry) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - int flag = 0; - char *p; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - retry: - p = smb_setup_header(req, SMBunlink, 1, 0); - WSET(req->rq_header, smb_vwv0, aSYSTEM | aHIDDEN); - result = smb_simple_encode_path(req, &p, dentry, NULL); - if (result < 0) - goto out_free; - smb_setup_bcc(req, p); - - if ((result = smb_request_ok(req, SMBunlink, 0, 0)) < 0) { -#if SMBFS_POSIX_UNLINK - if (result == -EACCES && !flag) { - /* Posix semantics is for the read-only state - of a file to be ignored in unlink(). In the - SMB world a unlink() is refused on a - read-only file. To make things easier for - unix users we try to override the files - permission if the unlink fails with the - right error. - This introduces a race condition that could - lead to a file being written by someone who - shouldn't have access, but as far as I can - tell that is unavoidable */ - - /* remove RONLY attribute and try again */ - result = smb_set_rw(dentry,server); - if (result == 0) { - flag = 1; - req->rq_flags = 0; - goto retry; - } - } -#endif - goto out_free; - } - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -int -smb_proc_flush(struct smb_sb_info *server, __u16 fileid) -{ - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - smb_setup_header(req, SMBflush, 1, 0); - WSET(req->rq_header, smb_vwv0, fileid); - req->rq_flags |= SMB_REQ_NORETRY; - result = smb_request_ok(req, SMBflush, 0, 0); - - smb_rput(req); -out: - return result; -} - -static int -smb_proc_trunc32(struct inode *inode, loff_t length) -{ - /* - * Writing 0bytes is old-SMB magic for truncating files. - * MAX_NON_LFS should prevent this from being called with a too - * large offset. - */ - return smb_proc_write(inode, length, 0, NULL); -} - -static int -smb_proc_trunc64(struct inode *inode, loff_t length) -{ - struct smb_sb_info *server = server_from_inode(inode); - int result; - char *param; - char *data; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 14))) - goto out; - - param = req->rq_buffer; - data = req->rq_buffer + 6; - - /* FIXME: must we also set allocation size? winNT seems to do that */ - WSET(param, 0, SMB_I(inode)->fileid); - WSET(param, 2, SMB_SET_FILE_END_OF_FILE_INFO); - WSET(param, 4, 0); - LSET(data, 0, length); - - req->rq_trans2_command = TRANSACT2_SETFILEINFO; - req->rq_ldata = 8; - req->rq_data = data; - req->rq_lparm = 6; - req->rq_parm = param; - req->rq_flags |= SMB_REQ_NORETRY; - result = smb_add_request(req); - if (result < 0) - goto out_free; - - result = 0; - if (req->rq_rcls != 0) - result = smb_errno(req); - -out_free: - smb_rput(req); -out: - return result; -} - -static int -smb_proc_trunc95(struct inode *inode, loff_t length) -{ - struct smb_sb_info *server = server_from_inode(inode); - int result = smb_proc_trunc32(inode, length); - - /* - * win9x doesn't appear to update the size immediately. - * It will return the old file size after the truncate, - * confusing smbfs. So we force an update. - * - * FIXME: is this still necessary? - */ - smb_proc_flush(server, SMB_I(inode)->fileid); - return result; -} - -static void -smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr) -{ - memset(fattr, 0, sizeof(*fattr)); - - fattr->f_nlink = 1; - fattr->f_uid = server->mnt->uid; - fattr->f_gid = server->mnt->gid; - fattr->f_unix = 0; -} - -static void -smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr) -{ - if (fattr->f_unix) - return; - - fattr->f_mode = server->mnt->file_mode; - if (fattr->attr & aDIR) { - fattr->f_mode = server->mnt->dir_mode; - fattr->f_size = SMB_ST_BLKSIZE; - } - /* Check the read-only flag */ - if (fattr->attr & aRONLY) - fattr->f_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); - - /* How many 512 byte blocks do we need for this file? */ - fattr->f_blocks = 0; - if (fattr->f_size != 0) - fattr->f_blocks = 1 + ((fattr->f_size-1) >> 9); - return; -} - -void -smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr, - struct super_block *sb) -{ - smb_init_dirent(server, fattr); - fattr->attr = aDIR; - fattr->f_ino = 2; /* traditional root inode number */ - fattr->f_mtime = current_fs_time(sb); - smb_finish_dirent(server, fattr); -} - -/* - * Decode a dirent for old protocols - * - * qname is filled with the decoded, and possibly translated, name. - * fattr receives decoded attributes - * - * Bugs Noted: - * (1) Pathworks servers may pad the name with extra spaces. - */ -static char * -smb_decode_short_dirent(struct smb_sb_info *server, char *p, - struct qstr *qname, struct smb_fattr *fattr, - unsigned char *name_buf) -{ - int len; - - /* - * SMB doesn't have a concept of inode numbers ... - */ - smb_init_dirent(server, fattr); - fattr->f_ino = 0; /* FIXME: do we need this? */ - - p += SMB_STATUS_SIZE; /* reserved (search_status) */ - fattr->attr = *p; - fattr->f_mtime.tv_sec = date_dos2unix(server, WVAL(p, 3), WVAL(p, 1)); - fattr->f_mtime.tv_nsec = 0; - fattr->f_size = DVAL(p, 5); - fattr->f_ctime = fattr->f_mtime; - fattr->f_atime = fattr->f_mtime; - qname->name = p + 9; - len = strnlen(qname->name, 12); - - /* - * Trim trailing blanks for Pathworks servers - */ - while (len > 2 && qname->name[len-1] == ' ') - len--; - - smb_finish_dirent(server, fattr); - -#if 0 - /* FIXME: These only work for ascii chars, and recent smbmount doesn't - allow the flag to be set anyway. It kills const. Remove? */ - switch (server->opt.case_handling) { - case SMB_CASE_UPPER: - str_upper(entry->name, len); - break; - case SMB_CASE_LOWER: - str_lower(entry->name, len); - break; - default: - break; - } -#endif - - qname->len = 0; - len = server->ops->convert(name_buf, SMB_MAXNAMELEN, - qname->name, len, - server->remote_nls, server->local_nls); - if (len > 0) { - qname->len = len; - qname->name = name_buf; - DEBUG1("len=%d, name=%.*s\n",qname->len,qname->len,qname->name); - } - - return p + 22; -} - -/* - * This routine is used to read in directory entries from the network. - * Note that it is for short directory name seeks, i.e.: protocol < - * SMB_PROTOCOL_LANMAN2 - */ -static int -smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir, - struct smb_cache_control *ctl) -{ - struct dentry *dir = filp->f_path.dentry; - struct smb_sb_info *server = server_from_dentry(dir); - struct qstr qname; - struct smb_fattr fattr; - char *p; - int result; - int i, first, entries_seen, entries; - int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE; - __u16 bcc; - __u16 count; - char status[SMB_STATUS_SIZE]; - static struct qstr mask = { - .name = "*.*", - .len = 3, - }; - unsigned char *last_status; - struct smb_request *req; - unsigned char *name_buf; - - VERBOSE("%s/%s\n", DENTRY_PATH(dir)); - - lock_kernel(); - - result = -ENOMEM; - if (! (name_buf = kmalloc(SMB_MAXNAMELEN, GFP_KERNEL))) - goto out; - - first = 1; - entries = 0; - entries_seen = 2; /* implicit . and .. */ - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, server->opt.max_xmit))) - goto out_name; - - while (1) { - p = smb_setup_header(req, SMBsearch, 2, 0); - WSET(req->rq_header, smb_vwv0, entries_asked); - WSET(req->rq_header, smb_vwv1, aDIR); - if (first == 1) { - result = smb_simple_encode_path(req, &p, dir, &mask); - if (result < 0) - goto out_free; - if (p + 3 > (char *)req->rq_buffer + req->rq_bufsize) { - result = -ENAMETOOLONG; - goto out_free; - } - *p++ = 5; - WSET(p, 0, 0); - p += 2; - first = 0; - } else { - if (p + 5 + SMB_STATUS_SIZE > - (char *)req->rq_buffer + req->rq_bufsize) { - result = -ENAMETOOLONG; - goto out_free; - } - - *p++ = 4; - *p++ = 0; - *p++ = 5; - WSET(p, 0, SMB_STATUS_SIZE); - p += 2; - memcpy(p, status, SMB_STATUS_SIZE); - p += SMB_STATUS_SIZE; - } - - smb_setup_bcc(req, p); - - result = smb_request_ok(req, SMBsearch, 1, -1); - if (result < 0) { - if ((req->rq_rcls == ERRDOS) && - (req->rq_err == ERRnofiles)) - break; - goto out_free; - } - count = WVAL(req->rq_header, smb_vwv0); - if (count <= 0) - break; - - result = -EIO; - bcc = smb_bcc(req->rq_header); - if (bcc != count * SMB_DIRINFO_SIZE + 3) - goto out_free; - p = req->rq_buffer + 3; - - - /* Make sure the response fits in the buffer. Fixed sized - entries means we don't have to check in the decode loop. */ - - last_status = req->rq_buffer + 3 + (count-1) * SMB_DIRINFO_SIZE; - - if (last_status + SMB_DIRINFO_SIZE >= - req->rq_buffer + req->rq_bufsize) { - printk(KERN_ERR "smb_proc_readdir_short: " - "last dir entry outside buffer! " - "%d@%p %d@%p\n", SMB_DIRINFO_SIZE, last_status, - req->rq_bufsize, req->rq_buffer); - goto out_free; - } - - /* Read the last entry into the status field. */ - memcpy(status, last_status, SMB_STATUS_SIZE); - - - /* Now we are ready to parse smb directory entries. */ - - for (i = 0; i < count; i++) { - p = smb_decode_short_dirent(server, p, - &qname, &fattr, name_buf); - if (qname.len == 0) - continue; - - if (entries_seen == 2 && qname.name[0] == '.') { - if (qname.len == 1) - continue; - if (qname.name[1] == '.' && qname.len == 2) - continue; - } - if (!smb_fill_cache(filp, dirent, filldir, ctl, - &qname, &fattr)) - ; /* stop reading? */ - entries_seen++; - } - } - result = entries; - -out_free: - smb_rput(req); -out_name: - kfree(name_buf); -out: - unlock_kernel(); - return result; -} - -static void smb_decode_unix_basic(struct smb_fattr *fattr, struct smb_sb_info *server, char *p) -{ - u64 size, disk_bytes; - - /* FIXME: verify nls support. all is sent as utf8? */ - - fattr->f_unix = 1; - fattr->f_mode = 0; - - /* FIXME: use the uniqueID from the remote instead? */ - /* 0 L file size in bytes */ - /* 8 L file size on disk in bytes (block count) */ - /* 40 L uid */ - /* 48 L gid */ - /* 56 W file type */ - /* 60 L devmajor */ - /* 68 L devminor */ - /* 76 L unique ID (inode) */ - /* 84 L permissions */ - /* 92 L link count */ - - size = LVAL(p, 0); - disk_bytes = LVAL(p, 8); - - /* - * Some samba versions round up on-disk byte usage - * to 1MB boundaries, making it useless. When seeing - * that, use the size instead. - */ - if (!(disk_bytes & 0xfffff)) - disk_bytes = size+511; - - fattr->f_size = size; - fattr->f_blocks = disk_bytes >> 9; - fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 16)); - fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 24)); - fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 32)); - - if (server->mnt->flags & SMB_MOUNT_UID) - fattr->f_uid = server->mnt->uid; - else - fattr->f_uid = LVAL(p, 40); - - if (server->mnt->flags & SMB_MOUNT_GID) - fattr->f_gid = server->mnt->gid; - else - fattr->f_gid = LVAL(p, 48); - - fattr->f_mode |= smb_filetype_to_mode(WVAL(p, 56)); - - if (S_ISBLK(fattr->f_mode) || S_ISCHR(fattr->f_mode)) { - __u64 major = LVAL(p, 60); - __u64 minor = LVAL(p, 68); - - fattr->f_rdev = MKDEV(major & 0xffffffff, minor & 0xffffffff); - if (MAJOR(fattr->f_rdev) != (major & 0xffffffff) || - MINOR(fattr->f_rdev) != (minor & 0xffffffff)) - fattr->f_rdev = 0; - } - - fattr->f_mode |= LVAL(p, 84); - - if ( (server->mnt->flags & SMB_MOUNT_DMODE) && - (S_ISDIR(fattr->f_mode)) ) - fattr->f_mode = (server->mnt->dir_mode & S_IRWXUGO) | S_IFDIR; - else if ( (server->mnt->flags & SMB_MOUNT_FMODE) && - !(S_ISDIR(fattr->f_mode)) ) - fattr->f_mode = (server->mnt->file_mode & S_IRWXUGO) | - (fattr->f_mode & S_IFMT); - -} - -/* - * Interpret a long filename structure using the specified info level: - * level 1 for anything below NT1 protocol - * level 260 for NT1 protocol - * - * qname is filled with the decoded, and possibly translated, name - * fattr receives decoded attributes. - * - * Bugs Noted: - * (1) Win NT 4.0 appends a null byte to names and counts it in the length! - */ -static char * -smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level, - struct qstr *qname, struct smb_fattr *fattr, - unsigned char *name_buf) -{ - char *result; - unsigned int len = 0; - int n; - __u16 date, time; - int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE); - - /* - * SMB doesn't have a concept of inode numbers ... - */ - smb_init_dirent(server, fattr); - fattr->f_ino = 0; /* FIXME: do we need this? */ - - switch (level) { - case 1: - len = *((unsigned char *) p + 22); - qname->name = p + 23; - result = p + 24 + len; - - date = WVAL(p, 0); - time = WVAL(p, 2); - fattr->f_ctime.tv_sec = date_dos2unix(server, date, time); - fattr->f_ctime.tv_nsec = 0; - - date = WVAL(p, 4); - time = WVAL(p, 6); - fattr->f_atime.tv_sec = date_dos2unix(server, date, time); - fattr->f_atime.tv_nsec = 0; - - date = WVAL(p, 8); - time = WVAL(p, 10); - fattr->f_mtime.tv_sec = date_dos2unix(server, date, time); - fattr->f_mtime.tv_nsec = 0; - fattr->f_size = DVAL(p, 12); - /* ULONG allocation size */ - fattr->attr = WVAL(p, 20); - - VERBOSE("info 1 at %p, len=%d, name=%.*s\n", - p, len, len, qname->name); - break; - case 260: - result = p + WVAL(p, 0); - len = DVAL(p, 60); - if (len > 255) len = 255; - /* NT4 null terminates, unless we are using unicode ... */ - qname->name = p + 94; - if (!unicode && len && qname->name[len-1] == '\0') - len--; - - fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 8)); - fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 16)); - fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 24)); - /* change time (32) */ - fattr->f_size = LVAL(p, 40); - /* alloc size (48) */ - fattr->attr = DVAL(p, 56); - - VERBOSE("info 260 at %p, len=%d, name=%.*s\n", - p, len, len, qname->name); - break; - case SMB_FIND_FILE_UNIX: - result = p + WVAL(p, 0); - qname->name = p + 108; - - len = strlen(qname->name); - /* FIXME: should we check the length?? */ - - p += 8; - smb_decode_unix_basic(fattr, server, p); - VERBOSE("info SMB_FIND_FILE_UNIX at %p, len=%d, name=%.*s\n", - p, len, len, qname->name); - break; - default: - PARANOIA("Unknown info level %d\n", level); - result = p + WVAL(p, 0); - goto out; - } - - smb_finish_dirent(server, fattr); - -#if 0 - /* FIXME: These only work for ascii chars, and recent smbmount doesn't - allow the flag to be set anyway. Remove? */ - switch (server->opt.case_handling) { - case SMB_CASE_UPPER: - str_upper(qname->name, len); - break; - case SMB_CASE_LOWER: - str_lower(qname->name, len); - break; - default: - break; - } -#endif - - qname->len = 0; - n = server->ops->convert(name_buf, SMB_MAXNAMELEN, - qname->name, len, - server->remote_nls, server->local_nls); - if (n > 0) { - qname->len = n; - qname->name = name_buf; - } - -out: - return result; -} - -/* findfirst/findnext flags */ -#define SMB_CLOSE_AFTER_FIRST (1<<0) -#define SMB_CLOSE_IF_END (1<<1) -#define SMB_REQUIRE_RESUME_KEY (1<<2) -#define SMB_CONTINUE_BIT (1<<3) - -/* - * Note: samba-2.0.7 (at least) has a very similar routine, cli_list, in - * source/libsmb/clilist.c. When looking for smb bugs in the readdir code, - * go there for advise. - * - * Bugs Noted: - * (1) When using Info Level 1 Win NT 4.0 truncates directory listings - * for certain patterns of names and/or lengths. The breakage pattern - * is completely reproducible and can be toggled by the creation of a - * single file. (E.g. echo hi >foo breaks, rm -f foo works.) - */ -static int -smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir, - struct smb_cache_control *ctl) -{ - struct dentry *dir = filp->f_path.dentry; - struct smb_sb_info *server = server_from_dentry(dir); - struct qstr qname; - struct smb_fattr fattr; - - unsigned char *p, *lastname; - char *mask, *param; - __u16 command; - int first, entries_seen; - - /* Both NT and OS/2 accept info level 1 (but see note below). */ - int info_level = 260; - const int max_matches = 512; - - unsigned int ff_searchcount = 0; - unsigned int ff_eos = 0; - unsigned int ff_lastname = 0; - unsigned int ff_dir_handle = 0; - unsigned int loop_count = 0; - unsigned int mask_len, i; - int result; - struct smb_request *req; - unsigned char *name_buf; - static struct qstr star = { - .name = "*", - .len = 1, - }; - - lock_kernel(); - - /* - * We always prefer unix style. Use info level 1 for older - * servers that don't do 260. - */ - if (server->opt.capabilities & SMB_CAP_UNIX) - info_level = SMB_FIND_FILE_UNIX; - else if (server->opt.protocol < SMB_PROTOCOL_NT1) - info_level = 1; - - result = -ENOMEM; - if (! (name_buf = kmalloc(SMB_MAXNAMELEN+2, GFP_KERNEL))) - goto out; - if (! (req = smb_alloc_request(server, server->opt.max_xmit))) - goto out_name; - param = req->rq_buffer; - - /* - * Encode the initial path - */ - mask = param + 12; - - result = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dir, &star); - if (result <= 0) - goto out_free; - mask_len = result - 1; /* mask_len is strlen, not #bytes */ - result = 0; - first = 1; - VERBOSE("starting mask_len=%d, mask=%s\n", mask_len, mask); - - entries_seen = 2; - ff_eos = 0; - - while (ff_eos == 0) { - loop_count += 1; - if (loop_count > 10) { - printk(KERN_WARNING "smb_proc_readdir_long: " - "Looping in FIND_NEXT??\n"); - result = -EIO; - break; - } - - if (first != 0) { - command = TRANSACT2_FINDFIRST; - WSET(param, 0, aSYSTEM | aHIDDEN | aDIR); - WSET(param, 2, max_matches); /* max count */ - WSET(param, 4, SMB_CLOSE_IF_END); - WSET(param, 6, info_level); - DSET(param, 8, 0); - } else { - command = TRANSACT2_FINDNEXT; - - VERBOSE("handle=0x%X, lastname=%d, mask=%.*s\n", - ff_dir_handle, ff_lastname, mask_len, mask); - - WSET(param, 0, ff_dir_handle); /* search handle */ - WSET(param, 2, max_matches); /* max count */ - WSET(param, 4, info_level); - DSET(param, 6, 0); - WSET(param, 10, SMB_CONTINUE_BIT|SMB_CLOSE_IF_END); - } - - req->rq_trans2_command = command; - req->rq_ldata = 0; - req->rq_data = NULL; - req->rq_lparm = 12 + mask_len + 1; - req->rq_parm = param; - req->rq_flags = 0; - result = smb_add_request(req); - if (result < 0) { - PARANOIA("error=%d, breaking\n", result); - break; - } - - if (req->rq_rcls == ERRSRV && req->rq_err == ERRerror) { - /* a damn Win95 bug - sometimes it clags if you - ask it too fast */ - schedule_timeout_interruptible(msecs_to_jiffies(200)); - continue; - } - - if (req->rq_rcls != 0) { - result = smb_errno(req); - PARANOIA("name=%s, result=%d, rcls=%d, err=%d\n", - mask, result, req->rq_rcls, req->rq_err); - break; - } - - /* parse out some important return info */ - if (first != 0) { - ff_dir_handle = WVAL(req->rq_parm, 0); - ff_searchcount = WVAL(req->rq_parm, 2); - ff_eos = WVAL(req->rq_parm, 4); - ff_lastname = WVAL(req->rq_parm, 8); - } else { - ff_searchcount = WVAL(req->rq_parm, 0); - ff_eos = WVAL(req->rq_parm, 2); - ff_lastname = WVAL(req->rq_parm, 6); - } - - if (ff_searchcount == 0) - break; - - /* Now we are ready to parse smb directory entries. */ - - /* point to the data bytes */ - p = req->rq_data; - for (i = 0; i < ff_searchcount; i++) { - /* make sure we stay within the buffer */ - if (p >= req->rq_data + req->rq_ldata) { - printk(KERN_ERR "smb_proc_readdir_long: " - "dirent pointer outside buffer! " - "%p %d@%p\n", - p, req->rq_ldata, req->rq_data); - result = -EIO; /* always a comm. error? */ - goto out_free; - } - - p = smb_decode_long_dirent(server, p, info_level, - &qname, &fattr, name_buf); - - /* ignore . and .. from the server */ - if (entries_seen == 2 && qname.name[0] == '.') { - if (qname.len == 1) - continue; - if (qname.name[1] == '.' && qname.len == 2) - continue; - } - - if (!smb_fill_cache(filp, dirent, filldir, ctl, - &qname, &fattr)) - ; /* stop reading? */ - entries_seen++; - } - - VERBOSE("received %d entries, eos=%d\n", ff_searchcount,ff_eos); - - /* - * We might need the lastname for continuations. - * - * Note that some servers (win95?) point to the filename and - * others (NT4, Samba using NT1) to the dir entry. We assume - * here that those who do not point to a filename do not need - * this info to continue the listing. - * - * OS/2 needs this and talks infolevel 1. - * NetApps want lastname with infolevel 260. - * win2k want lastname with infolevel 260, and points to - * the record not to the name. - * Samba+CifsUnixExt doesn't need lastname. - * - * Both are happy if we return the data they point to. So we do. - * (FIXME: above is not true with win2k) - */ - mask_len = 0; - if (info_level != SMB_FIND_FILE_UNIX && - ff_lastname > 0 && ff_lastname < req->rq_ldata) { - lastname = req->rq_data + ff_lastname; - - switch (info_level) { - case 260: - mask_len = req->rq_ldata - ff_lastname; - break; - case 1: - /* lastname points to a length byte */ - mask_len = *lastname++; - if (ff_lastname + 1 + mask_len > req->rq_ldata) - mask_len = req->rq_ldata - ff_lastname - 1; - break; - } - - /* - * Update the mask string for the next message. - */ - if (mask_len > 255) - mask_len = 255; - if (mask_len) - strncpy(mask, lastname, mask_len); - } - mask_len = strnlen(mask, mask_len); - VERBOSE("new mask, len=%d@%d of %d, mask=%.*s\n", - mask_len, ff_lastname, req->rq_ldata, mask_len, mask); - - first = 0; - loop_count = 0; - } - -out_free: - smb_rput(req); -out_name: - kfree(name_buf); -out: - unlock_kernel(); - return result; -} - -/* - * This version uses the trans2 TRANSACT2_FINDFIRST message - * to get the attribute data. - * - * Bugs Noted: - */ -static int -smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, - struct smb_fattr *fattr) -{ - char *param, *mask; - __u16 date, time; - int mask_len, result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - param = req->rq_buffer; - mask = param + 12; - - mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dentry,NULL); - if (mask_len < 0) { - result = mask_len; - goto out_free; - } - VERBOSE("name=%s, len=%d\n", mask, mask_len); - WSET(param, 0, aSYSTEM | aHIDDEN | aDIR); - WSET(param, 2, 1); /* max count */ - WSET(param, 4, 1); /* close after this call */ - WSET(param, 6, 1); /* info_level */ - DSET(param, 8, 0); - - req->rq_trans2_command = TRANSACT2_FINDFIRST; - req->rq_ldata = 0; - req->rq_data = NULL; - req->rq_lparm = 12 + mask_len; - req->rq_parm = param; - req->rq_flags = 0; - result = smb_add_request(req); - if (result < 0) - goto out_free; - if (req->rq_rcls != 0) { - result = smb_errno(req); -#ifdef SMBFS_PARANOIA - if (result != -ENOENT) - PARANOIA("error for %s, rcls=%d, err=%d\n", - mask, req->rq_rcls, req->rq_err); -#endif - goto out_free; - } - /* Make sure we got enough data ... */ - result = -EINVAL; - if (req->rq_ldata < 22 || WVAL(req->rq_parm, 2) != 1) { - PARANOIA("bad result for %s, len=%d, count=%d\n", - mask, req->rq_ldata, WVAL(req->rq_parm, 2)); - goto out_free; - } - - /* - * Decode the response into the fattr ... - */ - date = WVAL(req->rq_data, 0); - time = WVAL(req->rq_data, 2); - fattr->f_ctime.tv_sec = date_dos2unix(server, date, time); - fattr->f_ctime.tv_nsec = 0; - - date = WVAL(req->rq_data, 4); - time = WVAL(req->rq_data, 6); - fattr->f_atime.tv_sec = date_dos2unix(server, date, time); - fattr->f_atime.tv_nsec = 0; - - date = WVAL(req->rq_data, 8); - time = WVAL(req->rq_data, 10); - fattr->f_mtime.tv_sec = date_dos2unix(server, date, time); - fattr->f_mtime.tv_nsec = 0; - VERBOSE("name=%s, date=%x, time=%x, mtime=%ld\n", - mask, date, time, fattr->f_mtime.tv_sec); - fattr->f_size = DVAL(req->rq_data, 12); - /* ULONG allocation size */ - fattr->attr = WVAL(req->rq_data, 20); - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -static int -smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr) -{ - int result; - char *p; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - p = smb_setup_header(req, SMBgetatr, 0, 0); - result = smb_simple_encode_path(req, &p, dir, NULL); - if (result < 0) - goto out_free; - smb_setup_bcc(req, p); - - if ((result = smb_request_ok(req, SMBgetatr, 10, 0)) < 0) - goto out_free; - fattr->attr = WVAL(req->rq_header, smb_vwv0); - fattr->f_mtime.tv_sec = local2utc(server, DVAL(req->rq_header, smb_vwv1)); - fattr->f_mtime.tv_nsec = 0; - fattr->f_size = DVAL(req->rq_header, smb_vwv3); - fattr->f_ctime = fattr->f_mtime; - fattr->f_atime = fattr->f_mtime; -#ifdef SMBFS_DEBUG_TIMESTAMP - printk("getattr_core: %s/%s, mtime=%ld\n", - DENTRY_PATH(dir), fattr->f_mtime); -#endif - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -/* - * Bugs Noted: - * (1) Win 95 swaps the date and time fields in the standard info level. - */ -static int -smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, - struct smb_request *req, int infolevel) -{ - char *p, *param; - int result; - - param = req->rq_buffer; - WSET(param, 0, infolevel); - DSET(param, 2, 0); - result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL); - if (result < 0) - goto out; - p = param + 6 + result; - - req->rq_trans2_command = TRANSACT2_QPATHINFO; - req->rq_ldata = 0; - req->rq_data = NULL; - req->rq_lparm = p - param; - req->rq_parm = param; - req->rq_flags = 0; - result = smb_add_request(req); - if (result < 0) - goto out; - if (req->rq_rcls != 0) { - VERBOSE("for %s: result=%d, rcls=%d, err=%d\n", - ¶m[6], result, req->rq_rcls, req->rq_err); - result = smb_errno(req); - goto out; - } - result = -ENOENT; - if (req->rq_ldata < 22) { - PARANOIA("not enough data for %s, len=%d\n", - ¶m[6], req->rq_ldata); - goto out; - } - - result = 0; -out: - return result; -} - -static int -smb_proc_getattr_trans2_std(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *attr) -{ - u16 date, time; - int off_date = 0, off_time = 2; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - result = smb_proc_getattr_trans2(server, dir, req, SMB_INFO_STANDARD); - if (result < 0) - goto out_free; - - /* - * Kludge alert: Win 95 swaps the date and time field, - * contrary to the CIFS docs and Win NT practice. - */ - if (server->mnt->flags & SMB_MOUNT_WIN95) { - off_date = 2; - off_time = 0; - } - date = WVAL(req->rq_data, off_date); - time = WVAL(req->rq_data, off_time); - attr->f_ctime.tv_sec = date_dos2unix(server, date, time); - attr->f_ctime.tv_nsec = 0; - - date = WVAL(req->rq_data, 4 + off_date); - time = WVAL(req->rq_data, 4 + off_time); - attr->f_atime.tv_sec = date_dos2unix(server, date, time); - attr->f_atime.tv_nsec = 0; - - date = WVAL(req->rq_data, 8 + off_date); - time = WVAL(req->rq_data, 8 + off_time); - attr->f_mtime.tv_sec = date_dos2unix(server, date, time); - attr->f_mtime.tv_nsec = 0; -#ifdef SMBFS_DEBUG_TIMESTAMP - printk(KERN_DEBUG "getattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", - DENTRY_PATH(dir), date, time, attr->f_mtime); -#endif - attr->f_size = DVAL(req->rq_data, 12); - attr->attr = WVAL(req->rq_data, 20); - -out_free: - smb_rput(req); -out: - return result; -} - -static int -smb_proc_getattr_trans2_all(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *attr) -{ - struct smb_request *req; - int result; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - result = smb_proc_getattr_trans2(server, dir, req, - SMB_QUERY_FILE_ALL_INFO); - if (result < 0) - goto out_free; - - attr->f_ctime = smb_ntutc2unixutc(LVAL(req->rq_data, 0)); - attr->f_atime = smb_ntutc2unixutc(LVAL(req->rq_data, 8)); - attr->f_mtime = smb_ntutc2unixutc(LVAL(req->rq_data, 16)); - /* change (24) */ - attr->attr = WVAL(req->rq_data, 32); - /* pad? (34) */ - /* allocated size (40) */ - attr->f_size = LVAL(req->rq_data, 48); - -out_free: - smb_rput(req); -out: - return result; -} - -static int -smb_proc_getattr_unix(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *attr) -{ - struct smb_request *req; - int result; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - result = smb_proc_getattr_trans2(server, dir, req, - SMB_QUERY_FILE_UNIX_BASIC); - if (result < 0) - goto out_free; - - smb_decode_unix_basic(attr, server, req->rq_data); - -out_free: - smb_rput(req); -out: - return result; -} - -static int -smb_proc_getattr_95(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *attr) -{ - struct inode *inode = dir->d_inode; - int result; - - /* FIXME: why not use the "all" version? */ - result = smb_proc_getattr_trans2_std(server, dir, attr); - if (result < 0) - goto out; - - /* - * None of the getattr versions here can make win9x return the right - * filesize if there are changes made to an open file. - * A seek-to-end does return the right size, but we only need to do - * that on files we have written. - */ - if (inode && SMB_I(inode)->flags & SMB_F_LOCALWRITE && - smb_is_open(inode)) - { - __u16 fileid = SMB_I(inode)->fileid; - attr->f_size = smb_proc_seek(server, fileid, 2, 0); - } - -out: - return result; -} - -static int -smb_proc_ops_wait(struct smb_sb_info *server) -{ - int result; - - result = wait_event_interruptible_timeout(server->conn_wq, - server->conn_complete, 30*HZ); - - if (!result || signal_pending(current)) - return -EIO; - - return 0; -} - -static int -smb_proc_getattr_null(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr) -{ - int result; - - if (smb_proc_ops_wait(server) < 0) - return -EIO; - - smb_init_dirent(server, fattr); - result = server->ops->getattr(server, dir, fattr); - smb_finish_dirent(server, fattr); - - return result; -} - -static int -smb_proc_readdir_null(struct file *filp, void *dirent, filldir_t filldir, - struct smb_cache_control *ctl) -{ - struct smb_sb_info *server = server_from_dentry(filp->f_path.dentry); - - if (smb_proc_ops_wait(server) < 0) - return -EIO; - - return server->ops->readdir(filp, dirent, filldir, ctl); -} - -int -smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr) -{ - struct smb_sb_info *server = server_from_dentry(dir); - int result; - - smb_init_dirent(server, fattr); - result = server->ops->getattr(server, dir, fattr); - smb_finish_dirent(server, fattr); - - return result; -} - - -/* - * Because of bugs in the core protocol, we use this only to set - * attributes. See smb_proc_settime() below for timestamp handling. - * - * Bugs Noted: - * (1) If mtime is non-zero, both Win 3.1 and Win 95 fail - * with an undocumented error (ERRDOS code 50). Setting - * mtime to 0 allows the attributes to be set. - * (2) The extra parameters following the name string aren't - * in the CIFS docs, but seem to be necessary for operation. - */ -static int -smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, - __u16 attr) -{ - char *p; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - p = smb_setup_header(req, SMBsetatr, 8, 0); - WSET(req->rq_header, smb_vwv0, attr); - DSET(req->rq_header, smb_vwv1, 0); /* mtime */ - WSET(req->rq_header, smb_vwv3, 0); /* reserved values */ - WSET(req->rq_header, smb_vwv4, 0); - WSET(req->rq_header, smb_vwv5, 0); - WSET(req->rq_header, smb_vwv6, 0); - WSET(req->rq_header, smb_vwv7, 0); - result = smb_simple_encode_path(req, &p, dentry, NULL); - if (result < 0) - goto out_free; - if (p + 2 > (char *)req->rq_buffer + req->rq_bufsize) { - result = -ENAMETOOLONG; - goto out_free; - } - *p++ = 4; - *p++ = 0; - smb_setup_bcc(req, p); - - result = smb_request_ok(req, SMBsetatr, 0, 0); - if (result < 0) - goto out_free; - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -/* - * Because of bugs in the trans2 setattr messages, we must set - * attributes and timestamps separately. The core SMBsetatr - * message seems to be the only reliable way to set attributes. - */ -int -smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr) -{ - struct smb_sb_info *server = server_from_dentry(dir); - int result; - - VERBOSE("setting %s/%s, open=%d\n", - DENTRY_PATH(dir), smb_is_open(dir->d_inode)); - result = smb_proc_setattr_core(server, dir, fattr->attr); - return result; -} - -/* - * Sets the timestamps for an file open with write permissions. - */ -static int -smb_proc_setattr_ext(struct smb_sb_info *server, - struct inode *inode, struct smb_fattr *fattr) -{ - __u16 date, time; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - smb_setup_header(req, SMBsetattrE, 7, 0); - WSET(req->rq_header, smb_vwv0, SMB_I(inode)->fileid); - /* We don't change the creation time */ - WSET(req->rq_header, smb_vwv1, 0); - WSET(req->rq_header, smb_vwv2, 0); - date_unix2dos(server, fattr->f_atime.tv_sec, &date, &time); - WSET(req->rq_header, smb_vwv3, date); - WSET(req->rq_header, smb_vwv4, time); - date_unix2dos(server, fattr->f_mtime.tv_sec, &date, &time); - WSET(req->rq_header, smb_vwv5, date); - WSET(req->rq_header, smb_vwv6, time); -#ifdef SMBFS_DEBUG_TIMESTAMP - printk(KERN_DEBUG "smb_proc_setattr_ext: date=%d, time=%d, mtime=%ld\n", - date, time, fattr->f_mtime); -#endif - - req->rq_flags |= SMB_REQ_NORETRY; - result = smb_request_ok(req, SMBsetattrE, 0, 0); - if (result < 0) - goto out_free; - result = 0; -out_free: - smb_rput(req); -out: - return result; -} - -/* - * Bugs Noted: - * (1) The TRANSACT2_SETPATHINFO message under Win NT 4.0 doesn't - * set the file's attribute flags. - */ -static int -smb_proc_setattr_trans2(struct smb_sb_info *server, - struct dentry *dir, struct smb_fattr *fattr) -{ - __u16 date, time; - char *p, *param; - int result; - char data[26]; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - param = req->rq_buffer; - - WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ - DSET(param, 2, 0); - result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL); - if (result < 0) - goto out_free; - p = param + 6 + result; - - WSET(data, 0, 0); /* creation time */ - WSET(data, 2, 0); - date_unix2dos(server, fattr->f_atime.tv_sec, &date, &time); - WSET(data, 4, date); - WSET(data, 6, time); - date_unix2dos(server, fattr->f_mtime.tv_sec, &date, &time); - WSET(data, 8, date); - WSET(data, 10, time); -#ifdef SMBFS_DEBUG_TIMESTAMP - printk(KERN_DEBUG "setattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", - DENTRY_PATH(dir), date, time, fattr->f_mtime); -#endif - DSET(data, 12, 0); /* size */ - DSET(data, 16, 0); /* blksize */ - WSET(data, 20, 0); /* attr */ - DSET(data, 22, 0); /* ULONG EA size */ - - req->rq_trans2_command = TRANSACT2_SETPATHINFO; - req->rq_ldata = 26; - req->rq_data = data; - req->rq_lparm = p - param; - req->rq_parm = param; - req->rq_flags = 0; - result = smb_add_request(req); - if (result < 0) - goto out_free; - result = 0; - if (req->rq_rcls != 0) - result = smb_errno(req); - -out_free: - smb_rput(req); -out: - return result; -} - -/* - * ATTR_MODE 0x001 - * ATTR_UID 0x002 - * ATTR_GID 0x004 - * ATTR_SIZE 0x008 - * ATTR_ATIME 0x010 - * ATTR_MTIME 0x020 - * ATTR_CTIME 0x040 - * ATTR_ATIME_SET 0x080 - * ATTR_MTIME_SET 0x100 - * ATTR_FORCE 0x200 - * ATTR_ATTR_FLAG 0x400 - * - * major/minor should only be set by mknod. - */ -int -smb_proc_setattr_unix(struct dentry *d, struct iattr *attr, - unsigned int major, unsigned int minor) -{ - struct smb_sb_info *server = server_from_dentry(d); - u64 nttime; - char *p, *param; - int result; - char data[100]; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - param = req->rq_buffer; - - DEBUG1("valid flags = 0x%04x\n", attr->ia_valid); - - WSET(param, 0, SMB_SET_FILE_UNIX_BASIC); - DSET(param, 2, 0); - result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, d, NULL); - if (result < 0) - goto out_free; - p = param + 6 + result; - - /* 0 L file size in bytes */ - /* 8 L file size on disk in bytes (block count) */ - /* 40 L uid */ - /* 48 L gid */ - /* 56 W file type enum */ - /* 60 L devmajor */ - /* 68 L devminor */ - /* 76 L unique ID (inode) */ - /* 84 L permissions */ - /* 92 L link count */ - LSET(data, 0, SMB_SIZE_NO_CHANGE); - LSET(data, 8, SMB_SIZE_NO_CHANGE); - LSET(data, 16, SMB_TIME_NO_CHANGE); - LSET(data, 24, SMB_TIME_NO_CHANGE); - LSET(data, 32, SMB_TIME_NO_CHANGE); - LSET(data, 40, SMB_UID_NO_CHANGE); - LSET(data, 48, SMB_GID_NO_CHANGE); - DSET(data, 56, smb_filetype_from_mode(attr->ia_mode)); - LSET(data, 60, major); - LSET(data, 68, minor); - LSET(data, 76, 0); - LSET(data, 84, SMB_MODE_NO_CHANGE); - LSET(data, 92, 0); - - if (attr->ia_valid & ATTR_SIZE) { - LSET(data, 0, attr->ia_size); - LSET(data, 8, 0); /* can't set anyway */ - } - - /* - * FIXME: check the conversion function it the correct one - * - * we can't set ctime but we might as well pass this to the server - * and let it ignore it. - */ - if (attr->ia_valid & ATTR_CTIME) { - nttime = smb_unixutc2ntutc(attr->ia_ctime); - LSET(data, 16, nttime); - } - if (attr->ia_valid & ATTR_ATIME) { - nttime = smb_unixutc2ntutc(attr->ia_atime); - LSET(data, 24, nttime); - } - if (attr->ia_valid & ATTR_MTIME) { - nttime = smb_unixutc2ntutc(attr->ia_mtime); - LSET(data, 32, nttime); - } - - if (attr->ia_valid & ATTR_UID) { - LSET(data, 40, attr->ia_uid); - } - if (attr->ia_valid & ATTR_GID) { - LSET(data, 48, attr->ia_gid); - } - - if (attr->ia_valid & ATTR_MODE) { - LSET(data, 84, attr->ia_mode); - } - - req->rq_trans2_command = TRANSACT2_SETPATHINFO; - req->rq_ldata = 100; - req->rq_data = data; - req->rq_lparm = p - param; - req->rq_parm = param; - req->rq_flags = 0; - result = smb_add_request(req); - -out_free: - smb_rput(req); -out: - return result; -} - - -/* - * Set the modify and access timestamps for a file. - * - * Incredibly enough, in all of SMB there is no message to allow - * setting both attributes and timestamps at once. - * - * Bugs Noted: - * (1) Win 95 doesn't support the TRANSACT2_SETFILEINFO message - * with info level 1 (INFO_STANDARD). - * (2) Win 95 seems not to support setting directory timestamps. - * (3) Under the core protocol apparently the only way to set the - * timestamp is to open and close the file. - */ -int -smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - struct inode *inode = dentry->d_inode; - int result; - - VERBOSE("setting %s/%s, open=%d\n", - DENTRY_PATH(dentry), smb_is_open(inode)); - - /* setting the time on a Win95 server fails (tridge) */ - if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 && - !(server->mnt->flags & SMB_MOUNT_WIN95)) { - if (smb_is_open(inode) && SMB_I(inode)->access != SMB_O_RDONLY) - result = smb_proc_setattr_ext(server, inode, fattr); - else - result = smb_proc_setattr_trans2(server, dentry, fattr); - } else { - /* - * Fail silently on directories ... timestamp can't be set? - */ - result = 0; - if (S_ISREG(inode->i_mode)) { - /* - * Set the mtime by opening and closing the file. - * Note that the file is opened read-only, but this - * still allows us to set the date (tridge) - */ - result = -EACCES; - if (!smb_is_open(inode)) - smb_proc_open(server, dentry, SMB_O_RDONLY); - if (smb_is_open(inode)) { - inode->i_mtime = fattr->f_mtime; - result = smb_proc_close_inode(server, inode); - } - } - } - - return result; -} - -int -smb_proc_dskattr(struct dentry *dentry, struct kstatfs *attr) -{ - struct smb_sb_info *server = SMB_SB(dentry->d_sb); - int result; - char *p; - long unit; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - smb_setup_header(req, SMBdskattr, 0, 0); - if ((result = smb_request_ok(req, SMBdskattr, 5, 0)) < 0) - goto out_free; - p = SMB_VWV(req->rq_header); - unit = (WVAL(p, 2) * WVAL(p, 4)) >> SMB_ST_BLKSHIFT; - attr->f_blocks = WVAL(p, 0) * unit; - attr->f_bsize = SMB_ST_BLKSIZE; - attr->f_bavail = attr->f_bfree = WVAL(p, 6) * unit; - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -int -smb_proc_read_link(struct smb_sb_info *server, struct dentry *d, - char *buffer, int len) -{ - char *p, *param; - int result; - struct smb_request *req; - - DEBUG1("readlink of %s/%s\n", DENTRY_PATH(d)); - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - param = req->rq_buffer; - - WSET(param, 0, SMB_QUERY_FILE_UNIX_LINK); - DSET(param, 2, 0); - result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, d, NULL); - if (result < 0) - goto out_free; - p = param + 6 + result; - - req->rq_trans2_command = TRANSACT2_QPATHINFO; - req->rq_ldata = 0; - req->rq_data = NULL; - req->rq_lparm = p - param; - req->rq_parm = param; - req->rq_flags = 0; - result = smb_add_request(req); - if (result < 0) - goto out_free; - DEBUG1("for %s: result=%d, rcls=%d, err=%d\n", - ¶m[6], result, req->rq_rcls, req->rq_err); - - /* copy data up to the \0 or buffer length */ - result = len; - if (req->rq_ldata < len) - result = req->rq_ldata; - strncpy(buffer, req->rq_data, result); - -out_free: - smb_rput(req); -out: - return result; -} - - -/* - * Create a symlink object called dentry which points to oldpath. - * Samba does not permit dangling links but returns a suitable error message. - */ -int -smb_proc_symlink(struct smb_sb_info *server, struct dentry *d, - const char *oldpath) -{ - char *p, *param; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - param = req->rq_buffer; - - WSET(param, 0, SMB_SET_FILE_UNIX_LINK); - DSET(param, 2, 0); - result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+1, d, NULL); - if (result < 0) - goto out_free; - p = param + 6 + result; - - req->rq_trans2_command = TRANSACT2_SETPATHINFO; - req->rq_ldata = strlen(oldpath) + 1; - req->rq_data = (char *) oldpath; - req->rq_lparm = p - param; - req->rq_parm = param; - req->rq_flags = 0; - result = smb_add_request(req); - if (result < 0) - goto out_free; - - DEBUG1("for %s: result=%d, rcls=%d, err=%d\n", - ¶m[6], result, req->rq_rcls, req->rq_err); - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -/* - * Create a hard link object called new_dentry which points to dentry. - */ -int -smb_proc_link(struct smb_sb_info *server, struct dentry *dentry, - struct dentry *new_dentry) -{ - char *p, *param; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - param = req->rq_buffer; - - WSET(param, 0, SMB_SET_FILE_UNIX_HLINK); - DSET(param, 2, 0); - result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+1, - new_dentry, NULL); - if (result < 0) - goto out_free; - p = param + 6 + result; - - /* Grr, pointless separation of parameters and data ... */ - req->rq_data = p; - req->rq_ldata = smb_encode_path(server, p, SMB_MAXPATHLEN+1, - dentry, NULL); - - req->rq_trans2_command = TRANSACT2_SETPATHINFO; - req->rq_lparm = p - param; - req->rq_parm = param; - req->rq_flags = 0; - result = smb_add_request(req); - if (result < 0) - goto out_free; - - DEBUG1("for %s: result=%d, rcls=%d, err=%d\n", - ¶m[6], result, req->rq_rcls, req->rq_err); - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -static int -smb_proc_query_cifsunix(struct smb_sb_info *server) -{ - int result; - int major, minor; - u64 caps; - char param[2]; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 100))) - goto out; - - WSET(param, 0, SMB_QUERY_CIFS_UNIX_INFO); - - req->rq_trans2_command = TRANSACT2_QFSINFO; - req->rq_ldata = 0; - req->rq_data = NULL; - req->rq_lparm = 2; - req->rq_parm = param; - req->rq_flags = 0; - result = smb_add_request(req); - if (result < 0) - goto out_free; - - if (req->rq_ldata < 12) { - PARANOIA("Not enough data\n"); - goto out_free; - } - major = WVAL(req->rq_data, 0); - minor = WVAL(req->rq_data, 2); - - DEBUG1("Server implements CIFS Extensions for UNIX systems v%d.%d\n", - major, minor); - /* FIXME: verify that we are ok with this major/minor? */ - - caps = LVAL(req->rq_data, 4); - DEBUG1("Server capabilities 0x%016llx\n", caps); - -out_free: - smb_rput(req); -out: - return result; -} - - -static void -install_ops(struct smb_ops *dst, struct smb_ops *src) -{ - memcpy(dst, src, sizeof(void *) * SMB_OPS_NUM_STATIC); -} - -/* < LANMAN2 */ -static struct smb_ops smb_ops_core = -{ - .read = smb_proc_read, - .write = smb_proc_write, - .readdir = smb_proc_readdir_short, - .getattr = smb_proc_getattr_core, - .truncate = smb_proc_trunc32, -}; - -/* LANMAN2, OS/2, others? */ -static struct smb_ops smb_ops_os2 = -{ - .read = smb_proc_read, - .write = smb_proc_write, - .readdir = smb_proc_readdir_long, - .getattr = smb_proc_getattr_trans2_std, - .truncate = smb_proc_trunc32, -}; - -/* Win95, and possibly some NetApp versions too */ -static struct smb_ops smb_ops_win95 = -{ - .read = smb_proc_read, /* does not support 12word readX */ - .write = smb_proc_write, - .readdir = smb_proc_readdir_long, - .getattr = smb_proc_getattr_95, - .truncate = smb_proc_trunc95, -}; - -/* Samba, NT4 and NT5 */ -static struct smb_ops smb_ops_winNT = -{ - .read = smb_proc_readX, - .write = smb_proc_writeX, - .readdir = smb_proc_readdir_long, - .getattr = smb_proc_getattr_trans2_all, - .truncate = smb_proc_trunc64, -}; - -/* Samba w/ unix extensions. Others? */ -static struct smb_ops smb_ops_unix = -{ - .read = smb_proc_readX, - .write = smb_proc_writeX, - .readdir = smb_proc_readdir_long, - .getattr = smb_proc_getattr_unix, - /* FIXME: core/ext/time setattr needs to be cleaned up! */ - /* .setattr = smb_proc_setattr_unix, */ - .truncate = smb_proc_trunc64, -}; - -/* Place holder until real ops are in place */ -static struct smb_ops smb_ops_null = -{ - .readdir = smb_proc_readdir_null, - .getattr = smb_proc_getattr_null, -}; - -void smb_install_null_ops(struct smb_ops *ops) -{ - install_ops(ops, &smb_ops_null); -} diff --git a/fs/smbfs/proto.h b/fs/smbfs/proto.h deleted file mode 100644 index 05939a6f43e6..000000000000 --- a/fs/smbfs/proto.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Autogenerated with cproto on: Sat Sep 13 17:18:51 CEST 2003 - */ - -struct smb_request; -struct sock; -struct statfs; - -/* proc.c */ -extern int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp); -extern __u32 smb_len(__u8 *p); -extern int smb_get_rsize(struct smb_sb_info *server); -extern int smb_get_wsize(struct smb_sb_info *server); -extern int smb_errno(struct smb_request *req); -extern int smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt); -extern __u8 *smb_setup_header(struct smb_request *req, __u8 command, __u16 wct, __u16 bcc); -extern int smb_open(struct dentry *dentry, int wish); -extern int smb_close(struct inode *ino); -extern int smb_close_fileid(struct dentry *dentry, __u16 fileid); -extern int smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid); -extern int smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry); -extern int smb_proc_mkdir(struct dentry *dentry); -extern int smb_proc_rmdir(struct dentry *dentry); -extern int smb_proc_unlink(struct dentry *dentry); -extern int smb_proc_flush(struct smb_sb_info *server, __u16 fileid); -extern void smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr, - struct super_block *sb); -extern int smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr); -extern int smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr); -extern int smb_proc_setattr_unix(struct dentry *d, struct iattr *attr, unsigned int major, unsigned int minor); -extern int smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr); -extern int smb_proc_dskattr(struct dentry *dentry, struct kstatfs *attr); -extern int smb_proc_read_link(struct smb_sb_info *server, struct dentry *d, char *buffer, int len); -extern int smb_proc_symlink(struct smb_sb_info *server, struct dentry *d, const char *oldpath); -extern int smb_proc_link(struct smb_sb_info *server, struct dentry *dentry, struct dentry *new_dentry); -extern void smb_install_null_ops(struct smb_ops *ops); -/* dir.c */ -extern const struct file_operations smb_dir_operations; -extern const struct inode_operations smb_dir_inode_operations; -extern const struct inode_operations smb_dir_inode_operations_unix; -extern void smb_new_dentry(struct dentry *dentry); -extern void smb_renew_times(struct dentry *dentry); -/* cache.c */ -extern void smb_invalid_dir_cache(struct inode *dir); -extern void smb_invalidate_dircache_entries(struct dentry *parent); -extern struct dentry *smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos); -extern int smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctrl, struct qstr *qname, struct smb_fattr *entry); -/* sock.c */ -extern void smb_data_ready(struct sock *sk, int len); -extern int smb_valid_socket(struct inode *inode); -extern void smb_close_socket(struct smb_sb_info *server); -extern int smb_recv_available(struct smb_sb_info *server); -extern int smb_receive_header(struct smb_sb_info *server); -extern int smb_receive_drop(struct smb_sb_info *server); -extern int smb_receive(struct smb_sb_info *server, struct smb_request *req); -extern int smb_send_request(struct smb_request *req); -/* inode.c */ -extern struct inode *smb_iget(struct super_block *sb, struct smb_fattr *fattr); -extern void smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr); -extern void smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr); -extern void smb_invalidate_inodes(struct smb_sb_info *server); -extern int smb_revalidate_inode(struct dentry *dentry); -extern int smb_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); -extern int smb_notify_change(struct dentry *dentry, struct iattr *attr); -/* file.c */ -extern const struct address_space_operations smb_file_aops; -extern const struct file_operations smb_file_operations; -extern const struct inode_operations smb_file_inode_operations; -/* ioctl.c */ -extern long smb_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); -/* smbiod.c */ -extern void smbiod_wake_up(void); -extern int smbiod_register_server(struct smb_sb_info *server); -extern void smbiod_unregister_server(struct smb_sb_info *server); -extern void smbiod_flush(struct smb_sb_info *server); -extern int smbiod_retry(struct smb_sb_info *server); -/* request.c */ -extern int smb_init_request_cache(void); -extern void smb_destroy_request_cache(void); -extern struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize); -extern void smb_rput(struct smb_request *req); -extern int smb_add_request(struct smb_request *req); -extern int smb_request_send_server(struct smb_sb_info *server); -extern int smb_request_recv(struct smb_sb_info *server); -/* symlink.c */ -extern int smb_symlink(struct inode *inode, struct dentry *dentry, const char *oldname); -extern const struct inode_operations smb_link_inode_operations; diff --git a/fs/smbfs/request.c b/fs/smbfs/request.c deleted file mode 100644 index 45f45933e862..000000000000 --- a/fs/smbfs/request.c +++ /dev/null @@ -1,818 +0,0 @@ -/* - * request.c - * - * Copyright (C) 2001 by Urban Widmark - * - * Please add a note about your changes to smbfs in the ChangeLog file. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "smb_debug.h" -#include "request.h" -#include "proto.h" - -/* #define SMB_SLAB_DEBUG (SLAB_RED_ZONE | SLAB_POISON) */ -#define SMB_SLAB_DEBUG 0 - -/* cache for request structures */ -static struct kmem_cache *req_cachep; - -static int smb_request_send_req(struct smb_request *req); - -/* - /proc/slabinfo: - name, active, num, objsize, active_slabs, num_slaps, #pages -*/ - - -int smb_init_request_cache(void) -{ - req_cachep = kmem_cache_create("smb_request", - sizeof(struct smb_request), 0, - SMB_SLAB_DEBUG | SLAB_HWCACHE_ALIGN, - NULL); - if (req_cachep == NULL) - return -ENOMEM; - - return 0; -} - -void smb_destroy_request_cache(void) -{ - kmem_cache_destroy(req_cachep); -} - -/* - * Allocate and initialise a request structure - */ -static struct smb_request *smb_do_alloc_request(struct smb_sb_info *server, - int bufsize) -{ - struct smb_request *req; - unsigned char *buf = NULL; - - req = kmem_cache_zalloc(req_cachep, GFP_KERNEL); - VERBOSE("allocating request: %p\n", req); - if (!req) - goto out; - - if (bufsize > 0) { - buf = kmalloc(bufsize, GFP_NOFS); - if (!buf) { - kmem_cache_free(req_cachep, req); - return NULL; - } - } - - req->rq_buffer = buf; - req->rq_bufsize = bufsize; - req->rq_server = server; - init_waitqueue_head(&req->rq_wait); - INIT_LIST_HEAD(&req->rq_queue); - atomic_set(&req->rq_count, 1); - -out: - return req; -} - -struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize) -{ - struct smb_request *req = NULL; - - for (;;) { - atomic_inc(&server->nr_requests); - if (atomic_read(&server->nr_requests) <= MAX_REQUEST_HARD) { - req = smb_do_alloc_request(server, bufsize); - if (req != NULL) - break; - } - -#if 0 - /* - * Try to free up at least one request in order to stay - * below the hard limit - */ - if (nfs_try_to_free_pages(server)) - continue; - - if (fatal_signal_pending(current)) - return ERR_PTR(-ERESTARTSYS); - current->policy = SCHED_YIELD; - schedule(); -#else - /* FIXME: we want something like nfs does above, but that - requires changes to all callers and can wait. */ - break; -#endif - } - return req; -} - -static void smb_free_request(struct smb_request *req) -{ - atomic_dec(&req->rq_server->nr_requests); - if (req->rq_buffer && !(req->rq_flags & SMB_REQ_STATIC)) - kfree(req->rq_buffer); - kfree(req->rq_trans2buffer); - kmem_cache_free(req_cachep, req); -} - -/* - * What prevents a rget to race with a rput? The count must never drop to zero - * while it is in use. Only rput if it is ok that it is free'd. - */ -static void smb_rget(struct smb_request *req) -{ - atomic_inc(&req->rq_count); -} -void smb_rput(struct smb_request *req) -{ - if (atomic_dec_and_test(&req->rq_count)) { - list_del_init(&req->rq_queue); - smb_free_request(req); - } -} - -/* setup to receive the data part of the SMB */ -static int smb_setup_bcc(struct smb_request *req) -{ - int result = 0; - req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd; - - if (req->rq_rlen > req->rq_bufsize) { - PARANOIA("Packet too large %d > %d\n", - req->rq_rlen, req->rq_bufsize); - return -ENOBUFS; - } - - req->rq_iov[0].iov_base = req->rq_buffer; - req->rq_iov[0].iov_len = req->rq_rlen; - req->rq_iovlen = 1; - - return result; -} - -/* - * Prepare a "normal" request structure. - */ -static int smb_setup_request(struct smb_request *req) -{ - int len = smb_len(req->rq_header) + 4; - req->rq_slen = len; - - /* if we expect a data part in the reply we set the iov's to read it */ - if (req->rq_resp_bcc) - req->rq_setup_read = smb_setup_bcc; - - /* This tries to support re-using the same request */ - req->rq_bytes_sent = 0; - req->rq_rcls = 0; - req->rq_err = 0; - req->rq_errno = 0; - req->rq_fragment = 0; - kfree(req->rq_trans2buffer); - req->rq_trans2buffer = NULL; - - return 0; -} - -/* - * Prepare a transaction2 request structure - */ -static int smb_setup_trans2request(struct smb_request *req) -{ - struct smb_sb_info *server = req->rq_server; - int mparam, mdata; - static unsigned char padding[4]; - - /* I know the following is very ugly, but I want to build the - smb packet as efficiently as possible. */ - - const int smb_parameters = 15; - const int header = SMB_HEADER_LEN + 2 * smb_parameters + 2; - const int oparam = ALIGN(header + 3, sizeof(u32)); - const int odata = ALIGN(oparam + req->rq_lparm, sizeof(u32)); - const int bcc = (req->rq_data ? odata + req->rq_ldata : - oparam + req->rq_lparm) - header; - - if ((bcc + oparam) > server->opt.max_xmit) - return -ENOMEM; - smb_setup_header(req, SMBtrans2, smb_parameters, bcc); - - /* - * max parameters + max data + max setup == bufsize to make NT4 happy - * and not abort the transfer or split into multiple responses. It also - * makes smbfs happy as handling packets larger than the buffer size - * is extra work. - * - * OS/2 is probably going to hate me for this ... - */ - mparam = SMB_TRANS2_MAX_PARAM; - mdata = req->rq_bufsize - mparam; - - mdata = server->opt.max_xmit - mparam - 100; - if (mdata < 1024) { - mdata = 1024; - mparam = 20; - } - -#if 0 - /* NT/win2k has ~4k max_xmit, so with this we request more than it wants - to return as one SMB. Useful for testing the fragmented trans2 - handling. */ - mdata = 8192; -#endif - - WSET(req->rq_header, smb_tpscnt, req->rq_lparm); - WSET(req->rq_header, smb_tdscnt, req->rq_ldata); - WSET(req->rq_header, smb_mprcnt, mparam); - WSET(req->rq_header, smb_mdrcnt, mdata); - WSET(req->rq_header, smb_msrcnt, 0); /* max setup always 0 ? */ - WSET(req->rq_header, smb_flags, 0); - DSET(req->rq_header, smb_timeout, 0); - WSET(req->rq_header, smb_pscnt, req->rq_lparm); - WSET(req->rq_header, smb_psoff, oparam - 4); - WSET(req->rq_header, smb_dscnt, req->rq_ldata); - WSET(req->rq_header, smb_dsoff, req->rq_data ? odata - 4 : 0); - *(req->rq_header + smb_suwcnt) = 0x01; /* setup count */ - *(req->rq_header + smb_suwcnt + 1) = 0x00; /* reserved */ - WSET(req->rq_header, smb_setup0, req->rq_trans2_command); - - req->rq_iovlen = 2; - req->rq_iov[0].iov_base = (void *) req->rq_header; - req->rq_iov[0].iov_len = oparam; - req->rq_iov[1].iov_base = (req->rq_parm==NULL) ? padding : req->rq_parm; - req->rq_iov[1].iov_len = req->rq_lparm; - req->rq_slen = oparam + req->rq_lparm; - - if (req->rq_data) { - req->rq_iovlen += 2; - req->rq_iov[2].iov_base = padding; - req->rq_iov[2].iov_len = odata - oparam - req->rq_lparm; - req->rq_iov[3].iov_base = req->rq_data; - req->rq_iov[3].iov_len = req->rq_ldata; - req->rq_slen = odata + req->rq_ldata; - } - - /* always a data part for trans2 replies */ - req->rq_setup_read = smb_setup_bcc; - - return 0; -} - -/* - * Add a request and tell smbiod to process it - */ -int smb_add_request(struct smb_request *req) -{ - long timeleft; - struct smb_sb_info *server = req->rq_server; - int result = 0; - - smb_setup_request(req); - if (req->rq_trans2_command) { - if (req->rq_buffer == NULL) { - PARANOIA("trans2 attempted without response buffer!\n"); - return -EIO; - } - result = smb_setup_trans2request(req); - } - if (result < 0) - return result; - -#ifdef SMB_DEBUG_PACKET_SIZE - add_xmit_stats(req); -#endif - - /* add 'req' to the queue of requests */ - if (smb_lock_server_interruptible(server)) - return -EINTR; - - /* - * Try to send the request as the process. If that fails we queue the - * request and let smbiod send it later. - */ - - /* FIXME: each server has a number on the maximum number of parallel - requests. 10, 50 or so. We should not allow more requests to be - active. */ - if (server->mid > 0xf000) - server->mid = 0; - req->rq_mid = server->mid++; - WSET(req->rq_header, smb_mid, req->rq_mid); - - result = 0; - if (server->state == CONN_VALID) { - if (list_empty(&server->xmitq)) - result = smb_request_send_req(req); - if (result < 0) { - /* Connection lost? */ - server->conn_error = result; - server->state = CONN_INVALID; - } - } - if (result != 1) - list_add_tail(&req->rq_queue, &server->xmitq); - smb_rget(req); - - if (server->state != CONN_VALID) - smbiod_retry(server); - - smb_unlock_server(server); - - smbiod_wake_up(); - - timeleft = wait_event_interruptible_timeout(req->rq_wait, - req->rq_flags & SMB_REQ_RECEIVED, 30*HZ); - if (!timeleft || signal_pending(current)) { - /* - * On timeout or on interrupt we want to try and remove the - * request from the recvq/xmitq. - * First check if the request is still part of a queue. (May - * have been removed by some error condition) - */ - smb_lock_server(server); - if (!list_empty(&req->rq_queue)) { - list_del_init(&req->rq_queue); - smb_rput(req); - } - smb_unlock_server(server); - } - - if (!timeleft) { - PARANOIA("request [%p, mid=%d] timed out!\n", - req, req->rq_mid); - VERBOSE("smb_com: %02x\n", *(req->rq_header + smb_com)); - VERBOSE("smb_rcls: %02x\n", *(req->rq_header + smb_rcls)); - VERBOSE("smb_flg: %02x\n", *(req->rq_header + smb_flg)); - VERBOSE("smb_tid: %04x\n", WVAL(req->rq_header, smb_tid)); - VERBOSE("smb_pid: %04x\n", WVAL(req->rq_header, smb_pid)); - VERBOSE("smb_uid: %04x\n", WVAL(req->rq_header, smb_uid)); - VERBOSE("smb_mid: %04x\n", WVAL(req->rq_header, smb_mid)); - VERBOSE("smb_wct: %02x\n", *(req->rq_header + smb_wct)); - - req->rq_rcls = ERRSRV; - req->rq_err = ERRtimeout; - - /* Just in case it was "stuck" */ - smbiod_wake_up(); - } - VERBOSE("woke up, rcls=%d\n", req->rq_rcls); - - if (req->rq_rcls != 0) - req->rq_errno = smb_errno(req); - if (signal_pending(current)) - req->rq_errno = -ERESTARTSYS; - return req->rq_errno; -} - -/* - * Send a request and place it on the recvq if successfully sent. - * Must be called with the server lock held. - */ -static int smb_request_send_req(struct smb_request *req) -{ - struct smb_sb_info *server = req->rq_server; - int result; - - if (req->rq_bytes_sent == 0) { - WSET(req->rq_header, smb_tid, server->opt.tid); - WSET(req->rq_header, smb_pid, 1); - WSET(req->rq_header, smb_uid, server->opt.server_uid); - } - - result = smb_send_request(req); - if (result < 0 && result != -EAGAIN) - goto out; - - result = 0; - if (!(req->rq_flags & SMB_REQ_TRANSMITTED)) - goto out; - - list_move_tail(&req->rq_queue, &server->recvq); - result = 1; -out: - return result; -} - -/* - * Sends one request for this server. (smbiod) - * Must be called with the server lock held. - * Returns: <0 on error - * 0 if no request could be completely sent - * 1 if all data for one request was sent - */ -int smb_request_send_server(struct smb_sb_info *server) -{ - struct list_head *head; - struct smb_request *req; - int result; - - if (server->state != CONN_VALID) - return 0; - - /* dequeue first request, if any */ - req = NULL; - head = server->xmitq.next; - if (head != &server->xmitq) { - req = list_entry(head, struct smb_request, rq_queue); - } - if (!req) - return 0; - - result = smb_request_send_req(req); - if (result < 0) { - server->conn_error = result; - list_move(&req->rq_queue, &server->xmitq); - result = -EIO; - goto out; - } - -out: - return result; -} - -/* - * Try to find a request matching this "mid". Typically the first entry will - * be the matching one. - */ -static struct smb_request *find_request(struct smb_sb_info *server, int mid) -{ - struct list_head *tmp; - struct smb_request *req = NULL; - - list_for_each(tmp, &server->recvq) { - req = list_entry(tmp, struct smb_request, rq_queue); - if (req->rq_mid == mid) { - break; - } - req = NULL; - } - - if (!req) { - VERBOSE("received reply with mid %d but no request!\n", - WVAL(server->header, smb_mid)); - server->rstate = SMB_RECV_DROP; - } - - return req; -} - -/* - * Called when we have read the smb header and believe this is a response. - */ -static int smb_init_request(struct smb_sb_info *server, struct smb_request *req) -{ - int hdrlen, wct; - - memcpy(req->rq_header, server->header, SMB_HEADER_LEN); - - wct = *(req->rq_header + smb_wct); - if (wct > 20) { - PARANOIA("wct too large, %d > 20\n", wct); - server->rstate = SMB_RECV_DROP; - return 0; - } - - req->rq_resp_wct = wct; - hdrlen = SMB_HEADER_LEN + wct*2 + 2; - VERBOSE("header length: %d smb_wct: %2d\n", hdrlen, wct); - - req->rq_bytes_recvd = SMB_HEADER_LEN; - req->rq_rlen = hdrlen; - req->rq_iov[0].iov_base = req->rq_header; - req->rq_iov[0].iov_len = hdrlen; - req->rq_iovlen = 1; - server->rstate = SMB_RECV_PARAM; - -#ifdef SMB_DEBUG_PACKET_SIZE - add_recv_stats(smb_len(server->header)); -#endif - return 0; -} - -/* - * Reads the SMB parameters - */ -static int smb_recv_param(struct smb_sb_info *server, struct smb_request *req) -{ - int result; - - result = smb_receive(server, req); - if (result < 0) - return result; - if (req->rq_bytes_recvd < req->rq_rlen) - return 0; - - VERBOSE("result: %d smb_bcc: %04x\n", result, - WVAL(req->rq_header, SMB_HEADER_LEN + - (*(req->rq_header + smb_wct) * 2))); - - result = 0; - req->rq_iov[0].iov_base = NULL; - req->rq_rlen = 0; - if (req->rq_callback) - req->rq_callback(req); - else if (req->rq_setup_read) - result = req->rq_setup_read(req); - if (result < 0) { - server->rstate = SMB_RECV_DROP; - return result; - } - - server->rstate = req->rq_rlen > 0 ? SMB_RECV_DATA : SMB_RECV_END; - - req->rq_bytes_recvd = 0; // recvd out of the iov - - VERBOSE("rlen: %d\n", req->rq_rlen); - if (req->rq_rlen < 0) { - PARANOIA("Parameters read beyond end of packet!\n"); - server->rstate = SMB_RECV_END; - return -EIO; - } - return 0; -} - -/* - * Reads the SMB data - */ -static int smb_recv_data(struct smb_sb_info *server, struct smb_request *req) -{ - int result; - - result = smb_receive(server, req); - if (result < 0) - goto out; - if (req->rq_bytes_recvd < req->rq_rlen) - goto out; - server->rstate = SMB_RECV_END; -out: - VERBOSE("result: %d\n", result); - return result; -} - -/* - * Receive a transaction2 response - * Return: 0 if the response has been fully read - * 1 if there are further "fragments" to read - * <0 if there is an error - */ -static int smb_recv_trans2(struct smb_sb_info *server, struct smb_request *req) -{ - unsigned char *inbuf; - unsigned int parm_disp, parm_offset, parm_count, parm_tot; - unsigned int data_disp, data_offset, data_count, data_tot; - int hdrlen = SMB_HEADER_LEN + req->rq_resp_wct*2 - 2; - - VERBOSE("handling trans2\n"); - - inbuf = req->rq_header; - data_tot = WVAL(inbuf, smb_tdrcnt); - parm_tot = WVAL(inbuf, smb_tprcnt); - parm_disp = WVAL(inbuf, smb_prdisp); - parm_offset = WVAL(inbuf, smb_proff); - parm_count = WVAL(inbuf, smb_prcnt); - data_disp = WVAL(inbuf, smb_drdisp); - data_offset = WVAL(inbuf, smb_droff); - data_count = WVAL(inbuf, smb_drcnt); - - /* Modify offset for the split header/buffer we use */ - if (data_count || data_offset) { - if (unlikely(data_offset < hdrlen)) - goto out_bad_data; - else - data_offset -= hdrlen; - } - if (parm_count || parm_offset) { - if (unlikely(parm_offset < hdrlen)) - goto out_bad_parm; - else - parm_offset -= hdrlen; - } - - if (parm_count == parm_tot && data_count == data_tot) { - /* - * This packet has all the trans2 data. - * - * We setup the request so that this will be the common - * case. It may be a server error to not return a - * response that fits. - */ - VERBOSE("single trans2 response " - "dcnt=%u, pcnt=%u, doff=%u, poff=%u\n", - data_count, parm_count, - data_offset, parm_offset); - req->rq_ldata = data_count; - req->rq_lparm = parm_count; - req->rq_data = req->rq_buffer + data_offset; - req->rq_parm = req->rq_buffer + parm_offset; - if (unlikely(parm_offset + parm_count > req->rq_rlen)) - goto out_bad_parm; - if (unlikely(data_offset + data_count > req->rq_rlen)) - goto out_bad_data; - return 0; - } - - VERBOSE("multi trans2 response " - "frag=%d, dcnt=%u, pcnt=%u, doff=%u, poff=%u\n", - req->rq_fragment, - data_count, parm_count, - data_offset, parm_offset); - - if (!req->rq_fragment) { - int buf_len; - - /* We got the first trans2 fragment */ - req->rq_fragment = 1; - req->rq_total_data = data_tot; - req->rq_total_parm = parm_tot; - req->rq_ldata = 0; - req->rq_lparm = 0; - - buf_len = data_tot + parm_tot; - if (buf_len > SMB_MAX_PACKET_SIZE) - goto out_too_long; - - req->rq_trans2bufsize = buf_len; - req->rq_trans2buffer = kzalloc(buf_len, GFP_NOFS); - if (!req->rq_trans2buffer) - goto out_no_mem; - - req->rq_parm = req->rq_trans2buffer; - req->rq_data = req->rq_trans2buffer + parm_tot; - } else if (unlikely(req->rq_total_data < data_tot || - req->rq_total_parm < parm_tot)) - goto out_data_grew; - - if (unlikely(parm_disp + parm_count > req->rq_total_parm || - parm_offset + parm_count > req->rq_rlen)) - goto out_bad_parm; - if (unlikely(data_disp + data_count > req->rq_total_data || - data_offset + data_count > req->rq_rlen)) - goto out_bad_data; - - inbuf = req->rq_buffer; - memcpy(req->rq_parm + parm_disp, inbuf + parm_offset, parm_count); - memcpy(req->rq_data + data_disp, inbuf + data_offset, data_count); - - req->rq_ldata += data_count; - req->rq_lparm += parm_count; - - /* - * Check whether we've received all of the data. Note that - * we use the packet totals -- total lengths might shrink! - */ - if (req->rq_ldata >= data_tot && req->rq_lparm >= parm_tot) { - req->rq_ldata = data_tot; - req->rq_lparm = parm_tot; - return 0; - } - return 1; - -out_too_long: - printk(KERN_ERR "smb_trans2: data/param too long, data=%u, parm=%u\n", - data_tot, parm_tot); - goto out_EIO; -out_no_mem: - printk(KERN_ERR "smb_trans2: couldn't allocate data area of %d bytes\n", - req->rq_trans2bufsize); - req->rq_errno = -ENOMEM; - goto out; -out_data_grew: - printk(KERN_ERR "smb_trans2: data/params grew!\n"); - goto out_EIO; -out_bad_parm: - printk(KERN_ERR "smb_trans2: invalid parms, disp=%u, cnt=%u, tot=%u, ofs=%u\n", - parm_disp, parm_count, parm_tot, parm_offset); - goto out_EIO; -out_bad_data: - printk(KERN_ERR "smb_trans2: invalid data, disp=%u, cnt=%u, tot=%u, ofs=%u\n", - data_disp, data_count, data_tot, data_offset); -out_EIO: - req->rq_errno = -EIO; -out: - return req->rq_errno; -} - -/* - * State machine for receiving responses. We handle the fact that we can't - * read the full response in one try by having states telling us how much we - * have read. - * - * Must be called with the server lock held (only called from smbiod). - * - * Return: <0 on error - */ -int smb_request_recv(struct smb_sb_info *server) -{ - struct smb_request *req = NULL; - int result = 0; - - if (smb_recv_available(server) <= 0) - return 0; - - VERBOSE("state: %d\n", server->rstate); - switch (server->rstate) { - case SMB_RECV_DROP: - result = smb_receive_drop(server); - if (result < 0) - break; - if (server->rstate == SMB_RECV_DROP) - break; - server->rstate = SMB_RECV_START; - /* fallthrough */ - case SMB_RECV_START: - server->smb_read = 0; - server->rstate = SMB_RECV_HEADER; - /* fallthrough */ - case SMB_RECV_HEADER: - result = smb_receive_header(server); - if (result < 0) - break; - if (server->rstate == SMB_RECV_HEADER) - break; - if (! (*(server->header + smb_flg) & SMB_FLAGS_REPLY) ) { - server->rstate = SMB_RECV_REQUEST; - break; - } - if (server->rstate != SMB_RECV_HCOMPLETE) - break; - /* fallthrough */ - case SMB_RECV_HCOMPLETE: - req = find_request(server, WVAL(server->header, smb_mid)); - if (!req) - break; - smb_init_request(server, req); - req->rq_rcls = *(req->rq_header + smb_rcls); - req->rq_err = WVAL(req->rq_header, smb_err); - if (server->rstate != SMB_RECV_PARAM) - break; - /* fallthrough */ - case SMB_RECV_PARAM: - if (!req) - req = find_request(server,WVAL(server->header,smb_mid)); - if (!req) - break; - result = smb_recv_param(server, req); - if (result < 0) - break; - if (server->rstate != SMB_RECV_DATA) - break; - /* fallthrough */ - case SMB_RECV_DATA: - if (!req) - req = find_request(server,WVAL(server->header,smb_mid)); - if (!req) - break; - result = smb_recv_data(server, req); - if (result < 0) - break; - break; - - /* We should never be called with any of these states */ - case SMB_RECV_END: - case SMB_RECV_REQUEST: - BUG(); - } - - if (result < 0) { - /* We saw an error */ - return result; - } - - if (server->rstate != SMB_RECV_END) - return 0; - - result = 0; - if (req->rq_trans2_command && req->rq_rcls == SUCCESS) - result = smb_recv_trans2(server, req); - - /* - * Response completely read. Drop any extra bytes sent by the server. - * (Yes, servers sometimes add extra bytes to responses) - */ - VERBOSE("smb_len: %d smb_read: %d\n", - server->smb_len, server->smb_read); - if (server->smb_read < server->smb_len) - smb_receive_drop(server); - - server->rstate = SMB_RECV_START; - - if (!result) { - list_del_init(&req->rq_queue); - req->rq_flags |= SMB_REQ_RECEIVED; - smb_rput(req); - wake_up_interruptible(&req->rq_wait); - } - return 0; -} diff --git a/fs/smbfs/request.h b/fs/smbfs/request.h deleted file mode 100644 index efb21451e7c9..000000000000 --- a/fs/smbfs/request.h +++ /dev/null @@ -1,70 +0,0 @@ -#include -#include -#include -#include - -struct smb_request { - struct list_head rq_queue; /* recvq or xmitq for the server */ - - atomic_t rq_count; - - wait_queue_head_t rq_wait; - int rq_flags; - int rq_mid; /* multiplex ID, set by request.c */ - - struct smb_sb_info *rq_server; - - /* header + word count + parameter words + byte count */ - unsigned char rq_header[SMB_HEADER_LEN + 20*2 + 2]; - - int rq_bufsize; - unsigned char *rq_buffer; - - /* FIXME: this is not good enough for merging IO requests. */ - unsigned char *rq_page; - int rq_rsize; - - int rq_resp_wct; - int rq_resp_bcc; - - int rq_rlen; - int rq_bytes_recvd; - - int rq_slen; - int rq_bytes_sent; - - int rq_iovlen; - struct kvec rq_iov[4]; - - int (*rq_setup_read) (struct smb_request *); - void (*rq_callback) (struct smb_request *); - - /* ------ trans2 stuff ------ */ - - u16 rq_trans2_command; /* 0 if not a trans2 request */ - unsigned int rq_ldata; - unsigned char *rq_data; - unsigned int rq_lparm; - unsigned char *rq_parm; - - int rq_fragment; - u32 rq_total_data; - u32 rq_total_parm; - int rq_trans2bufsize; - unsigned char *rq_trans2buffer; - - /* ------ response ------ */ - - unsigned short rq_rcls; - unsigned short rq_err; - int rq_errno; -}; - -#define SMB_REQ_STATIC 0x0001 /* rq_buffer is static */ -#define SMB_REQ_NORETRY 0x0002 /* request is invalid after retry */ - -#define SMB_REQ_TRANSMITTED 0x4000 /* all data has been sent */ -#define SMB_REQ_RECEIVED 0x8000 /* reply received, smbiod is done */ - -#define xSMB_REQ_NOREPLY 0x0004 /* we don't want the reply (if any) */ -#define xSMB_REQ_NORECEIVER 0x0008 /* caller doesn't wait for response */ diff --git a/fs/smbfs/smb_debug.h b/fs/smbfs/smb_debug.h deleted file mode 100644 index fc4b1a5dd755..000000000000 --- a/fs/smbfs/smb_debug.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Defines some debug macros for smbfs. - */ - -/* This makes a dentry parent/child name pair. Useful for debugging printk's */ -#define DENTRY_PATH(dentry) \ - (dentry)->d_parent->d_name.name,(dentry)->d_name.name - -/* - * safety checks that should never happen ??? - * these are normally enabled. - */ -#ifdef SMBFS_PARANOIA -# define PARANOIA(f, a...) printk(KERN_NOTICE "%s: " f, __func__ , ## a) -#else -# define PARANOIA(f, a...) do { ; } while(0) -#endif - -/* lots of debug messages */ -#ifdef SMBFS_DEBUG_VERBOSE -# define VERBOSE(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a) -#else -# define VERBOSE(f, a...) do { ; } while(0) -#endif - -/* - * "normal" debug messages, but not with a normal DEBUG define ... way - * too common name. - */ -#ifdef SMBFS_DEBUG -#define DEBUG1(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a) -#else -#define DEBUG1(f, a...) do { ; } while(0) -#endif diff --git a/fs/smbfs/smbiod.c b/fs/smbfs/smbiod.c deleted file mode 100644 index 0e39a924f10a..000000000000 --- a/fs/smbfs/smbiod.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * smbiod.c - * - * Copyright (C) 2000, Charles Loep / Corel Corp. - * Copyright (C) 2001, Urban Widmark - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "smb_debug.h" -#include "request.h" -#include "proto.h" - -enum smbiod_state { - SMBIOD_DEAD, - SMBIOD_STARTING, - SMBIOD_RUNNING, -}; - -static enum smbiod_state smbiod_state = SMBIOD_DEAD; -static struct task_struct *smbiod_thread; -static DECLARE_WAIT_QUEUE_HEAD(smbiod_wait); -static LIST_HEAD(smb_servers); -static DEFINE_SPINLOCK(servers_lock); - -#define SMBIOD_DATA_READY (1<<0) -static unsigned long smbiod_flags; - -static int smbiod(void *); -static int smbiod_start(void); - -/* - * called when there's work for us to do - */ -void smbiod_wake_up(void) -{ - if (smbiod_state == SMBIOD_DEAD) - return; - set_bit(SMBIOD_DATA_READY, &smbiod_flags); - wake_up_interruptible(&smbiod_wait); -} - -/* - * start smbiod if none is running - */ -static int smbiod_start(void) -{ - struct task_struct *tsk; - int err = 0; - - if (smbiod_state != SMBIOD_DEAD) - return 0; - smbiod_state = SMBIOD_STARTING; - __module_get(THIS_MODULE); - spin_unlock(&servers_lock); - tsk = kthread_run(smbiod, NULL, "smbiod"); - if (IS_ERR(tsk)) { - err = PTR_ERR(tsk); - module_put(THIS_MODULE); - } - - spin_lock(&servers_lock); - if (err < 0) { - smbiod_state = SMBIOD_DEAD; - smbiod_thread = NULL; - } else { - smbiod_state = SMBIOD_RUNNING; - smbiod_thread = tsk; - } - return err; -} - -/* - * register a server & start smbiod if necessary - */ -int smbiod_register_server(struct smb_sb_info *server) -{ - int ret; - spin_lock(&servers_lock); - list_add(&server->entry, &smb_servers); - VERBOSE("%p\n", server); - ret = smbiod_start(); - spin_unlock(&servers_lock); - return ret; -} - -/* - * Unregister a server - * Must be called with the server lock held. - */ -void smbiod_unregister_server(struct smb_sb_info *server) -{ - spin_lock(&servers_lock); - list_del_init(&server->entry); - VERBOSE("%p\n", server); - spin_unlock(&servers_lock); - - smbiod_wake_up(); - smbiod_flush(server); -} - -void smbiod_flush(struct smb_sb_info *server) -{ - struct list_head *tmp, *n; - struct smb_request *req; - - list_for_each_safe(tmp, n, &server->xmitq) { - req = list_entry(tmp, struct smb_request, rq_queue); - req->rq_errno = -EIO; - list_del_init(&req->rq_queue); - smb_rput(req); - wake_up_interruptible(&req->rq_wait); - } - list_for_each_safe(tmp, n, &server->recvq) { - req = list_entry(tmp, struct smb_request, rq_queue); - req->rq_errno = -EIO; - list_del_init(&req->rq_queue); - smb_rput(req); - wake_up_interruptible(&req->rq_wait); - } -} - -/* - * Wake up smbmount and make it reconnect to the server. - * This must be called with the server locked. - * - * FIXME: add smbconnect version to this - */ -int smbiod_retry(struct smb_sb_info *server) -{ - struct list_head *head; - struct smb_request *req; - struct pid *pid = get_pid(server->conn_pid); - int result = 0; - - VERBOSE("state: %d\n", server->state); - if (server->state == CONN_VALID || server->state == CONN_RETRYING) - goto out; - - smb_invalidate_inodes(server); - - /* - * Some requests are meaningless after a retry, so we abort them. - * One example are all requests using 'fileid' since the files are - * closed on retry. - */ - head = server->xmitq.next; - while (head != &server->xmitq) { - req = list_entry(head, struct smb_request, rq_queue); - head = head->next; - - req->rq_bytes_sent = 0; - if (req->rq_flags & SMB_REQ_NORETRY) { - VERBOSE("aborting request %p on xmitq\n", req); - req->rq_errno = -EIO; - list_del_init(&req->rq_queue); - smb_rput(req); - wake_up_interruptible(&req->rq_wait); - } - } - - /* - * FIXME: test the code for retrying request we already sent - */ - head = server->recvq.next; - while (head != &server->recvq) { - req = list_entry(head, struct smb_request, rq_queue); - head = head->next; -#if 0 - if (req->rq_flags & SMB_REQ_RETRY) { - /* must move the request to the xmitq */ - VERBOSE("retrying request %p on recvq\n", req); - list_move(&req->rq_queue, &server->xmitq); - continue; - } -#endif - - VERBOSE("aborting request %p on recvq\n", req); - /* req->rq_rcls = ???; */ /* FIXME: set smb error code too? */ - req->rq_errno = -EIO; - list_del_init(&req->rq_queue); - smb_rput(req); - wake_up_interruptible(&req->rq_wait); - } - - smb_close_socket(server); - - if (!pid) { - /* FIXME: this is fatal, umount? */ - printk(KERN_ERR "smb_retry: no connection process\n"); - server->state = CONN_RETRIED; - goto out; - } - - /* - * Change state so that only one retry per server will be started. - */ - server->state = CONN_RETRYING; - - /* - * Note: use the "priv" flag, as a user process may need to reconnect. - */ - result = kill_pid(pid, SIGUSR1, 1); - if (result) { - /* FIXME: this is most likely fatal, umount? */ - printk(KERN_ERR "smb_retry: signal failed [%d]\n", result); - goto out; - } - VERBOSE("signalled pid %d\n", pid_nr(pid)); - - /* FIXME: The retried requests should perhaps get a "time boost". */ - -out: - put_pid(pid); - return result; -} - -/* - * Currently handles lockingX packets. - */ -static void smbiod_handle_request(struct smb_sb_info *server) -{ - PARANOIA("smbiod got a request ... and we don't implement oplocks!\n"); - server->rstate = SMB_RECV_DROP; -} - -/* - * Do some IO for one server. - */ -static void smbiod_doio(struct smb_sb_info *server) -{ - int result; - int maxwork = 7; - - if (server->state != CONN_VALID) - goto out; - - do { - result = smb_request_recv(server); - if (result < 0) { - server->state = CONN_INVALID; - smbiod_retry(server); - goto out; /* reconnecting is slow */ - } else if (server->rstate == SMB_RECV_REQUEST) - smbiod_handle_request(server); - } while (result > 0 && maxwork-- > 0); - - /* - * If there is more to read then we want to be sure to wake up again. - */ - if (server->state != CONN_VALID) - goto out; - if (smb_recv_available(server) > 0) - set_bit(SMBIOD_DATA_READY, &smbiod_flags); - - do { - result = smb_request_send_server(server); - if (result < 0) { - server->state = CONN_INVALID; - smbiod_retry(server); - goto out; /* reconnecting is slow */ - } - } while (result > 0); - - /* - * If the last request was not sent out we want to wake up again. - */ - if (!list_empty(&server->xmitq)) - set_bit(SMBIOD_DATA_READY, &smbiod_flags); - -out: - return; -} - -/* - * smbiod kernel thread - */ -static int smbiod(void *unused) -{ - VERBOSE("SMB Kernel thread starting (%d) ...\n", current->pid); - - for (;;) { - struct smb_sb_info *server; - struct list_head *pos, *n; - - /* FIXME: Use poll? */ - wait_event_interruptible(smbiod_wait, - test_bit(SMBIOD_DATA_READY, &smbiod_flags)); - if (signal_pending(current)) { - spin_lock(&servers_lock); - smbiod_state = SMBIOD_DEAD; - spin_unlock(&servers_lock); - break; - } - - clear_bit(SMBIOD_DATA_READY, &smbiod_flags); - - spin_lock(&servers_lock); - if (list_empty(&smb_servers)) { - smbiod_state = SMBIOD_DEAD; - spin_unlock(&servers_lock); - break; - } - - list_for_each_safe(pos, n, &smb_servers) { - server = list_entry(pos, struct smb_sb_info, entry); - VERBOSE("checking server %p\n", server); - - if (server->state == CONN_VALID) { - spin_unlock(&servers_lock); - - smb_lock_server(server); - smbiod_doio(server); - smb_unlock_server(server); - - spin_lock(&servers_lock); - } - } - spin_unlock(&servers_lock); - } - - VERBOSE("SMB Kernel thread exiting (%d) ...\n", current->pid); - module_put_and_exit(0); -} diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c deleted file mode 100644 index e37fe4deebd0..000000000000 --- a/fs/smbfs/sock.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * sock.c - * - * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - * Please add a note about your changes to smbfs in the ChangeLog file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "smb_debug.h" -#include "proto.h" -#include "request.h" - - -static int -_recvfrom(struct socket *socket, unsigned char *ubuf, int size, unsigned flags) -{ - struct kvec iov = {ubuf, size}; - struct msghdr msg = {.msg_flags = flags}; - msg.msg_flags |= MSG_DONTWAIT | MSG_NOSIGNAL; - return kernel_recvmsg(socket, &msg, &iov, 1, size, msg.msg_flags); -} - -/* - * Return the server this socket belongs to - */ -static struct smb_sb_info * -server_from_socket(struct socket *socket) -{ - return socket->sk->sk_user_data; -} - -/* - * Called when there is data on the socket. - */ -void -smb_data_ready(struct sock *sk, int len) -{ - struct smb_sb_info *server = server_from_socket(sk->sk_socket); - void (*data_ready)(struct sock *, int) = server->data_ready; - - data_ready(sk, len); - VERBOSE("(%p, %d)\n", sk, len); - smbiod_wake_up(); -} - -int -smb_valid_socket(struct inode * inode) -{ - return (inode && S_ISSOCK(inode->i_mode) && - SOCKET_I(inode)->type == SOCK_STREAM); -} - -static struct socket * -server_sock(struct smb_sb_info *server) -{ - struct file *file; - - if (server && (file = server->sock_file)) - { -#ifdef SMBFS_PARANOIA - if (!smb_valid_socket(file->f_path.dentry->d_inode)) - PARANOIA("bad socket!\n"); -#endif - return SOCKET_I(file->f_path.dentry->d_inode); - } - return NULL; -} - -void -smb_close_socket(struct smb_sb_info *server) -{ - struct file * file = server->sock_file; - - if (file) { - struct socket *sock = server_sock(server); - - VERBOSE("closing socket %p\n", sock); - sock->sk->sk_data_ready = server->data_ready; - server->sock_file = NULL; - fput(file); - } -} - -static int -smb_get_length(struct socket *socket, unsigned char *header) -{ - int result; - - result = _recvfrom(socket, header, 4, MSG_PEEK); - if (result == -EAGAIN) - return -ENODATA; - if (result < 0) { - PARANOIA("recv error = %d\n", -result); - return result; - } - if (result < 4) - return -ENODATA; - - switch (header[0]) { - case 0x00: - case 0x82: - break; - - case 0x85: - DEBUG1("Got SESSION KEEP ALIVE\n"); - _recvfrom(socket, header, 4, 0); /* read away */ - return -ENODATA; - - default: - PARANOIA("Invalid NBT packet, code=%x\n", header[0]); - return -EIO; - } - - /* The length in the RFC NB header is the raw data length */ - return smb_len(header); -} - -int -smb_recv_available(struct smb_sb_info *server) -{ - mm_segment_t oldfs; - int avail, err; - struct socket *sock = server_sock(server); - - oldfs = get_fs(); - set_fs(get_ds()); - err = sock->ops->ioctl(sock, SIOCINQ, (unsigned long) &avail); - set_fs(oldfs); - return (err >= 0) ? avail : err; -} - -/* - * Adjust the kvec to move on 'n' bytes (from nfs/sunrpc) - */ -static int -smb_move_iov(struct kvec **data, size_t *num, struct kvec *vec, unsigned amount) -{ - struct kvec *iv = *data; - int i; - int len; - - /* - * Eat any sent kvecs - */ - while (iv->iov_len <= amount) { - amount -= iv->iov_len; - iv++; - (*num)--; - } - - /* - * And chew down the partial one - */ - vec[0].iov_len = iv->iov_len-amount; - vec[0].iov_base =((unsigned char *)iv->iov_base)+amount; - iv++; - - len = vec[0].iov_len; - - /* - * And copy any others - */ - for (i = 1; i < *num; i++) { - vec[i] = *iv++; - len += vec[i].iov_len; - } - - *data = vec; - return len; -} - -/* - * smb_receive_header - * Only called by the smbiod thread. - */ -int -smb_receive_header(struct smb_sb_info *server) -{ - struct socket *sock; - int result = 0; - unsigned char peek_buf[4]; - - result = -EIO; - sock = server_sock(server); - if (!sock) - goto out; - if (sock->sk->sk_state != TCP_ESTABLISHED) - goto out; - - if (!server->smb_read) { - result = smb_get_length(sock, peek_buf); - if (result < 0) { - if (result == -ENODATA) - result = 0; - goto out; - } - server->smb_len = result + 4; - - if (server->smb_len < SMB_HEADER_LEN) { - PARANOIA("short packet: %d\n", result); - server->rstate = SMB_RECV_DROP; - result = -EIO; - goto out; - } - if (server->smb_len > SMB_MAX_PACKET_SIZE) { - PARANOIA("long packet: %d\n", result); - server->rstate = SMB_RECV_DROP; - result = -EIO; - goto out; - } - } - - result = _recvfrom(sock, server->header + server->smb_read, - SMB_HEADER_LEN - server->smb_read, 0); - VERBOSE("_recvfrom: %d\n", result); - if (result < 0) { - VERBOSE("receive error: %d\n", result); - goto out; - } - server->smb_read += result; - - if (server->smb_read == SMB_HEADER_LEN) - server->rstate = SMB_RECV_HCOMPLETE; -out: - return result; -} - -static char drop_buffer[PAGE_SIZE]; - -/* - * smb_receive_drop - read and throw away the data - * Only called by the smbiod thread. - * - * FIXME: we are in the kernel, could we just tell the socket that we want - * to drop stuff from the buffer? - */ -int -smb_receive_drop(struct smb_sb_info *server) -{ - struct socket *sock; - unsigned int flags; - struct kvec iov; - struct msghdr msg; - int rlen = smb_len(server->header) - server->smb_read + 4; - int result = -EIO; - - if (rlen > PAGE_SIZE) - rlen = PAGE_SIZE; - - sock = server_sock(server); - if (!sock) - goto out; - if (sock->sk->sk_state != TCP_ESTABLISHED) - goto out; - - flags = MSG_DONTWAIT | MSG_NOSIGNAL; - iov.iov_base = drop_buffer; - iov.iov_len = PAGE_SIZE; - msg.msg_flags = flags; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_control = NULL; - - result = kernel_recvmsg(sock, &msg, &iov, 1, rlen, flags); - - VERBOSE("read: %d\n", result); - if (result < 0) { - VERBOSE("receive error: %d\n", result); - goto out; - } - server->smb_read += result; - - if (server->smb_read >= server->smb_len) - server->rstate = SMB_RECV_END; - -out: - return result; -} - -/* - * smb_receive - * Only called by the smbiod thread. - */ -int -smb_receive(struct smb_sb_info *server, struct smb_request *req) -{ - struct socket *sock; - unsigned int flags; - struct kvec iov[4]; - struct kvec *p = req->rq_iov; - size_t num = req->rq_iovlen; - struct msghdr msg; - int rlen; - int result = -EIO; - - sock = server_sock(server); - if (!sock) - goto out; - if (sock->sk->sk_state != TCP_ESTABLISHED) - goto out; - - flags = MSG_DONTWAIT | MSG_NOSIGNAL; - msg.msg_flags = flags; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_control = NULL; - - /* Dont repeat bytes and count available bufferspace */ - rlen = min_t(int, smb_move_iov(&p, &num, iov, req->rq_bytes_recvd), - (req->rq_rlen - req->rq_bytes_recvd)); - - result = kernel_recvmsg(sock, &msg, p, num, rlen, flags); - - VERBOSE("read: %d\n", result); - if (result < 0) { - VERBOSE("receive error: %d\n", result); - goto out; - } - req->rq_bytes_recvd += result; - server->smb_read += result; - -out: - return result; -} - -/* - * Try to send a SMB request. This may return after sending only parts of the - * request. SMB_REQ_TRANSMITTED will be set if a request was fully sent. - * - * Parts of this was taken from xprt_sendmsg from net/sunrpc/xprt.c - */ -int -smb_send_request(struct smb_request *req) -{ - struct smb_sb_info *server = req->rq_server; - struct socket *sock; - struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT}; - int slen = req->rq_slen - req->rq_bytes_sent; - int result = -EIO; - struct kvec iov[4]; - struct kvec *p = req->rq_iov; - size_t num = req->rq_iovlen; - - sock = server_sock(server); - if (!sock) - goto out; - if (sock->sk->sk_state != TCP_ESTABLISHED) - goto out; - - /* Dont repeat bytes */ - if (req->rq_bytes_sent) - smb_move_iov(&p, &num, iov, req->rq_bytes_sent); - - result = kernel_sendmsg(sock, &msg, p, num, slen); - - if (result >= 0) { - req->rq_bytes_sent += result; - if (req->rq_bytes_sent >= req->rq_slen) - req->rq_flags |= SMB_REQ_TRANSMITTED; - } -out: - return result; -} diff --git a/fs/smbfs/symlink.c b/fs/smbfs/symlink.c deleted file mode 100644 index 00b2909bd469..000000000000 --- a/fs/smbfs/symlink.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * symlink.c - * - * Copyright (C) 2002 by John Newbigin - * - * Please add a note about your changes to smbfs in the ChangeLog file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "smb_debug.h" -#include "proto.h" - -int smb_symlink(struct inode *inode, struct dentry *dentry, const char *oldname) -{ - DEBUG1("create symlink %s -> %s/%s\n", oldname, DENTRY_PATH(dentry)); - - return smb_proc_symlink(server_from_dentry(dentry), dentry, oldname); -} - -static void *smb_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - char *link = __getname(); - DEBUG1("followlink of %s/%s\n", DENTRY_PATH(dentry)); - - if (!link) { - link = ERR_PTR(-ENOMEM); - } else { - int len = smb_proc_read_link(server_from_dentry(dentry), - dentry, link, PATH_MAX - 1); - if (len < 0) { - __putname(link); - link = ERR_PTR(len); - } else { - link[len] = 0; - } - } - nd_set_link(nd, link); - return NULL; -} - -static void smb_put_link(struct dentry *dentry, struct nameidata *nd, void *p) -{ - char *s = nd_get_link(nd); - if (!IS_ERR(s)) - __putname(s); -} - -const struct inode_operations smb_link_inode_operations = -{ - .readlink = generic_readlink, - .follow_link = smb_follow_link, - .put_link = smb_put_link, -}; diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 626b629429ff..98d520d371ed 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -326,10 +326,6 @@ header-y += serio.h header-y += shm.h header-y += signal.h header-y += signalfd.h -header-y += smb.h -header-y += smb_fs.h -header-y += smb_mount.h -header-y += smbno.h header-y += snmp.h header-y += socket.h header-y += sockios.h diff --git a/include/linux/smb.h b/include/linux/smb.h deleted file mode 100644 index 82fefddc5987..000000000000 --- a/include/linux/smb.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * smb.h - * - * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - */ - -#ifndef _LINUX_SMB_H -#define _LINUX_SMB_H - -#include -#include -#ifdef __KERNEL__ -#include -#endif - -enum smb_protocol { - SMB_PROTOCOL_NONE, - SMB_PROTOCOL_CORE, - SMB_PROTOCOL_COREPLUS, - SMB_PROTOCOL_LANMAN1, - SMB_PROTOCOL_LANMAN2, - SMB_PROTOCOL_NT1 -}; - -enum smb_case_hndl { - SMB_CASE_DEFAULT, - SMB_CASE_LOWER, - SMB_CASE_UPPER -}; - -struct smb_dskattr { - __u16 total; - __u16 allocblocks; - __u16 blocksize; - __u16 free; -}; - -struct smb_conn_opt { - - /* The socket */ - unsigned int fd; - - enum smb_protocol protocol; - enum smb_case_hndl case_handling; - - /* Connection-Options */ - - __u32 max_xmit; - __u16 server_uid; - __u16 tid; - - /* The following are LANMAN 1.0 options */ - __u16 secmode; - __u16 maxmux; - __u16 maxvcs; - __u16 rawmode; - __u32 sesskey; - - /* The following are NT LM 0.12 options */ - __u32 maxraw; - __u32 capabilities; - __s16 serverzone; -}; - -#ifdef __KERNEL__ - -#define SMB_NLS_MAXNAMELEN 20 -struct smb_nls_codepage { - char local_name[SMB_NLS_MAXNAMELEN]; - char remote_name[SMB_NLS_MAXNAMELEN]; -}; - - -#define SMB_MAXNAMELEN 255 -#define SMB_MAXPATHLEN 1024 - -/* - * Contains all relevant data on a SMB networked file. - */ -struct smb_fattr { - __u16 attr; - - unsigned long f_ino; - umode_t f_mode; - nlink_t f_nlink; - uid_t f_uid; - gid_t f_gid; - dev_t f_rdev; - loff_t f_size; - struct timespec f_atime; - struct timespec f_mtime; - struct timespec f_ctime; - unsigned long f_blocks; - int f_unix; -}; - -enum smb_conn_state { - CONN_VALID, /* everything's fine */ - CONN_INVALID, /* Something went wrong, but did not - try to reconnect yet. */ - CONN_RETRIED, /* Tried a reconnection, but was refused */ - CONN_RETRYING /* Currently trying to reconnect */ -}; - -#define SMB_HEADER_LEN 37 /* includes everything up to, but not - * including smb_bcc */ - -#define SMB_INITIAL_PACKET_SIZE 4000 -#define SMB_MAX_PACKET_SIZE 32768 - -/* reserve this much space for trans2 parameters. Shouldn't have to be more - than 10 or so, but OS/2 seems happier like this. */ -#define SMB_TRANS2_MAX_PARAM 64 - -#endif -#endif diff --git a/include/linux/smb_fs.h b/include/linux/smb_fs.h deleted file mode 100644 index 923cd8a247b1..000000000000 --- a/include/linux/smb_fs.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * smb_fs.h - * - * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - */ - -#ifndef _LINUX_SMB_FS_H -#define _LINUX_SMB_FS_H - -#include - -/* - * ioctl commands - */ -#define SMB_IOC_GETMOUNTUID _IOR('u', 1, __kernel_old_uid_t) -#define SMB_IOC_NEWCONN _IOW('u', 2, struct smb_conn_opt) - -/* __kernel_uid_t can never change, so we have to use __kernel_uid32_t */ -#define SMB_IOC_GETMOUNTUID32 _IOR('u', 3, __kernel_uid32_t) - - -#ifdef __KERNEL__ -#include -#include - -#include -#include -#include -#include -#include -#include - -static inline struct smb_sb_info *SMB_SB(struct super_block *sb) -{ - return sb->s_fs_info; -} - -static inline struct smb_inode_info *SMB_I(struct inode *inode) -{ - return container_of(inode, struct smb_inode_info, vfs_inode); -} - -/* macro names are short for word, double-word, long value (?) */ -#define WVAL(buf, pos) (get_unaligned_le16((u8 *)(buf) + (pos))) -#define DVAL(buf, pos) (get_unaligned_le32((u8 *)(buf) + (pos))) -#define LVAL(buf, pos) (get_unaligned_le64((u8 *)(buf) + (pos))) - -#define WSET(buf, pos, val) put_unaligned_le16((val), (u8 *)(buf) + (pos)) -#define DSET(buf, pos, val) put_unaligned_le32((val), (u8 *)(buf) + (pos)) -#define LSET(buf, pos, val) put_unaligned_le64((val), (u8 *)(buf) + (pos)) - -/* where to find the base of the SMB packet proper */ -#define smb_base(buf) ((u8 *)(((u8 *)(buf))+4)) - -/* - * Flags for the in-memory inode - */ -#define SMB_F_LOCALWRITE 0x02 /* file modified locally */ - - -/* NT1 protocol capability bits */ -#define SMB_CAP_RAW_MODE 0x00000001 -#define SMB_CAP_MPX_MODE 0x00000002 -#define SMB_CAP_UNICODE 0x00000004 -#define SMB_CAP_LARGE_FILES 0x00000008 -#define SMB_CAP_NT_SMBS 0x00000010 -#define SMB_CAP_RPC_REMOTE_APIS 0x00000020 -#define SMB_CAP_STATUS32 0x00000040 -#define SMB_CAP_LEVEL_II_OPLOCKS 0x00000080 -#define SMB_CAP_LOCK_AND_READ 0x00000100 -#define SMB_CAP_NT_FIND 0x00000200 -#define SMB_CAP_DFS 0x00001000 -#define SMB_CAP_LARGE_READX 0x00004000 -#define SMB_CAP_LARGE_WRITEX 0x00008000 -#define SMB_CAP_UNIX 0x00800000 /* unofficial ... */ - - -/* - * This is the time we allow an inode, dentry or dir cache to live. It is bad - * for performance to have shorter ttl on an inode than on the cache. It can - * cause refresh on each inode for a dir listing ... one-by-one - */ -#define SMB_MAX_AGE(server) (((server)->mnt->ttl * HZ) / 1000) - -static inline void -smb_age_dentry(struct smb_sb_info *server, struct dentry *dentry) -{ - dentry->d_time = jiffies - SMB_MAX_AGE(server); -} - -struct smb_cache_head { - time_t mtime; /* unused */ - unsigned long time; /* cache age */ - unsigned long end; /* last valid fpos in cache */ - int eof; -}; - -#define SMB_DIRCACHE_SIZE ((int)(PAGE_CACHE_SIZE/sizeof(struct dentry *))) -union smb_dir_cache { - struct smb_cache_head head; - struct dentry *dentry[SMB_DIRCACHE_SIZE]; -}; - -#define SMB_FIRSTCACHE_SIZE ((int)((SMB_DIRCACHE_SIZE * \ - sizeof(struct dentry *) - sizeof(struct smb_cache_head)) / \ - sizeof(struct dentry *))) - -#define SMB_DIRCACHE_START (SMB_DIRCACHE_SIZE - SMB_FIRSTCACHE_SIZE) - -struct smb_cache_control { - struct smb_cache_head head; - struct page *page; - union smb_dir_cache *cache; - unsigned long fpos, ofs; - int filled, valid, idx; -}; - -#define SMB_OPS_NUM_STATIC 5 -struct smb_ops { - int (*read)(struct inode *inode, loff_t offset, int count, - char *data); - int (*write)(struct inode *inode, loff_t offset, int count, const - char *data); - int (*readdir)(struct file *filp, void *dirent, filldir_t filldir, - struct smb_cache_control *ctl); - - int (*getattr)(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr); - /* int (*setattr)(...); */ /* setattr is really icky! */ - - int (*truncate)(struct inode *inode, loff_t length); - - - /* --- --- --- end of "static" entries --- --- --- */ - - int (*convert)(unsigned char *output, int olen, - const unsigned char *input, int ilen, - struct nls_table *nls_from, - struct nls_table *nls_to); -}; - -static inline int -smb_is_open(struct inode *i) -{ - return (SMB_I(i)->open == server_from_inode(i)->generation); -} - -extern void smb_install_null_ops(struct smb_ops *); -#endif /* __KERNEL__ */ - -#endif /* _LINUX_SMB_FS_H */ diff --git a/include/linux/smb_fs_i.h b/include/linux/smb_fs_i.h deleted file mode 100644 index 8ccf4eca2c3d..000000000000 --- a/include/linux/smb_fs_i.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * smb_fs_i.h - * - * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - */ - -#ifndef _LINUX_SMB_FS_I -#define _LINUX_SMB_FS_I - -#include -#include - -/* - * smb fs inode data (in memory only) - */ -struct smb_inode_info { - - /* - * file handles are local to a connection. A file is open if - * (open == generation). - */ - unsigned int open; /* open generation */ - __u16 fileid; /* What id to handle a file with? */ - __u16 attr; /* Attribute fields, DOS value */ - - __u16 access; /* Access mode */ - __u16 flags; - unsigned long oldmtime; /* last time refreshed */ - unsigned long closed; /* timestamp when closed */ - unsigned openers; /* number of fileid users */ - - struct inode vfs_inode; /* must be at the end */ -}; - -#endif diff --git a/include/linux/smb_fs_sb.h b/include/linux/smb_fs_sb.h deleted file mode 100644 index bb947dd1fba9..000000000000 --- a/include/linux/smb_fs_sb.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * smb_fs_sb.h - * - * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - */ - -#ifndef _SMB_FS_SB -#define _SMB_FS_SB - -#include -#include -#include - -/* - * Upper limit on the total number of active smb_request structs. - */ -#define MAX_REQUEST_HARD 256 - -enum smb_receive_state { - SMB_RECV_START, /* No data read, looking for length + sig */ - SMB_RECV_HEADER, /* Reading the header data */ - SMB_RECV_HCOMPLETE, /* Done with the header */ - SMB_RECV_PARAM, /* Reading parameter words */ - SMB_RECV_DATA, /* Reading data bytes */ - SMB_RECV_END, /* End of request */ - SMB_RECV_DROP, /* Dropping this SMB */ - SMB_RECV_REQUEST, /* Received a request and not a reply */ -}; - -/* structure access macros */ -#define server_from_inode(inode) SMB_SB((inode)->i_sb) -#define server_from_dentry(dentry) SMB_SB((dentry)->d_sb) -#define SB_of(server) ((server)->super_block) - -struct smb_sb_info { - /* List of all smbfs superblocks */ - struct list_head entry; - - enum smb_conn_state state; - struct file * sock_file; - int conn_error; - enum smb_receive_state rstate; - - atomic_t nr_requests; - struct list_head xmitq; - struct list_head recvq; - u16 mid; - - struct smb_mount_data_kernel *mnt; - - /* Connections are counted. Each time a new socket arrives, - * generation is incremented. - */ - unsigned int generation; - struct pid *conn_pid; - struct smb_conn_opt opt; - wait_queue_head_t conn_wq; - int conn_complete; - struct semaphore sem; - - unsigned char header[SMB_HEADER_LEN + 20*2 + 2]; - u32 header_len; - u32 smb_len; - u32 smb_read; - - /* We use our own data_ready callback, but need the original one */ - void *data_ready; - - /* nls pointers for codepage conversions */ - struct nls_table *remote_nls; - struct nls_table *local_nls; - - struct smb_ops *ops; - - struct super_block *super_block; - - struct backing_dev_info bdi; -}; - -static inline int -smb_lock_server_interruptible(struct smb_sb_info *server) -{ - return down_interruptible(&(server->sem)); -} - -static inline void -smb_lock_server(struct smb_sb_info *server) -{ - down(&(server->sem)); -} - -static inline void -smb_unlock_server(struct smb_sb_info *server) -{ - up(&(server->sem)); -} - -#endif diff --git a/include/linux/smb_mount.h b/include/linux/smb_mount.h deleted file mode 100644 index d10f00cb5703..000000000000 --- a/include/linux/smb_mount.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * smb_mount.h - * - * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - */ - -#ifndef _LINUX_SMB_MOUNT_H -#define _LINUX_SMB_MOUNT_H - -#include - -#define SMB_MOUNT_VERSION 6 - -struct smb_mount_data { - int version; - __kernel_uid_t mounted_uid; /* Who may umount() this filesystem? */ - __kernel_uid_t uid; - __kernel_gid_t gid; - __kernel_mode_t file_mode; - __kernel_mode_t dir_mode; -}; - - -#ifdef __KERNEL__ - -/* "vers" in big-endian */ -#define SMB_MOUNT_ASCII 0x76657273 - -#define SMB_MOUNT_OLDVERSION 6 -#undef SMB_MOUNT_VERSION -#define SMB_MOUNT_VERSION 7 - -/* flags */ -#define SMB_MOUNT_WIN95 0x0001 /* Win 95 server */ -#define SMB_MOUNT_OLDATTR 0x0002 /* Use core getattr (Win 95 speedup) */ -#define SMB_MOUNT_DIRATTR 0x0004 /* Use find_first for getattr */ -#define SMB_MOUNT_CASE 0x0008 /* Be case sensitive */ -#define SMB_MOUNT_UNICODE 0x0010 /* Server talks unicode */ -#define SMB_MOUNT_UID 0x0020 /* Use user specified uid */ -#define SMB_MOUNT_GID 0x0040 /* Use user specified gid */ -#define SMB_MOUNT_FMODE 0x0080 /* Use user specified file mode */ -#define SMB_MOUNT_DMODE 0x0100 /* Use user specified dir mode */ - -struct smb_mount_data_kernel { - int version; - - uid_t mounted_uid; /* Who may umount() this filesystem? */ - uid_t uid; - gid_t gid; - mode_t file_mode; - mode_t dir_mode; - - u32 flags; - - /* maximum age in jiffies (inode, dentry and dircache) */ - int ttl; - - struct smb_nls_codepage codepage; -}; - -#endif - -#endif diff --git a/include/linux/smbno.h b/include/linux/smbno.h deleted file mode 100644 index f99e02d9ffe2..000000000000 --- a/include/linux/smbno.h +++ /dev/null @@ -1,363 +0,0 @@ -#ifndef _SMBNO_H_ -#define _SMBNO_H_ - -/* these define the attribute byte as seen by DOS */ -#define aRONLY (1L<<0) -#define aHIDDEN (1L<<1) -#define aSYSTEM (1L<<2) -#define aVOLID (1L<<3) -#define aDIR (1L<<4) -#define aARCH (1L<<5) - -/* error classes */ -#define SUCCESS 0 /* The request was successful. */ -#define ERRDOS 0x01 /* Error is from the core DOS operating system set. */ -#define ERRSRV 0x02 /* Error is generated by the server network file manager.*/ -#define ERRHRD 0x03 /* Error is an hardware error. */ -#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ - -/* SMB X/Open error codes for the ERRdos error class */ - -#define ERRbadfunc 1 /* Invalid function (or system call) */ -#define ERRbadfile 2 /* File not found (pathname error) */ -#define ERRbadpath 3 /* Directory not found */ -#define ERRnofids 4 /* Too many open files */ -#define ERRnoaccess 5 /* Access denied */ -#define ERRbadfid 6 /* Invalid fid */ -#define ERRbadmcb 7 /* Memory control blocks destroyed */ -#define ERRnomem 8 /* Out of memory */ -#define ERRbadmem 9 /* Invalid memory block address */ -#define ERRbadenv 10 /* Invalid environment */ -#define ERRbadformat 11 /* Invalid format */ -#define ERRbadaccess 12 /* Invalid open mode */ -#define ERRbaddata 13 /* Invalid data (only from ioctl call) */ -#define ERRres 14 /* reserved */ -#define ERRbaddrive 15 /* Invalid drive */ -#define ERRremcd 16 /* Attempt to delete current directory */ -#define ERRdiffdevice 17 /* rename/move across different filesystems */ -#define ERRnofiles 18 /* no more files found in file search */ -#define ERRbadshare 32 /* Share mode on file conflict with open mode */ -#define ERRlock 33 /* Lock request conflicts with existing lock */ -#define ERRfilexists 80 /* File in operation already exists */ -#define ERRbadpipe 230 /* Named pipe invalid */ -#define ERRpipebusy 231 /* All instances of pipe are busy */ -#define ERRpipeclosing 232 /* named pipe close in progress */ -#define ERRnotconnected 233 /* No process on other end of named pipe */ -#define ERRmoredata 234 /* More data to be returned */ - -#define ERROR_INVALID_PARAMETER 87 -#define ERROR_DISK_FULL 112 -#define ERROR_INVALID_NAME 123 -#define ERROR_DIR_NOT_EMPTY 145 -#define ERROR_NOT_LOCKED 158 -#define ERROR_ALREADY_EXISTS 183 /* see also 80 ? */ -#define ERROR_EAS_DIDNT_FIT 275 /* Extended attributes didn't fit */ -#define ERROR_EAS_NOT_SUPPORTED 282 /* Extended attributes not supported */ - -/* Error codes for the ERRSRV class */ - -#define ERRerror 1 /* Non specific error code */ -#define ERRbadpw 2 /* Bad password */ -#define ERRbadtype 3 /* reserved */ -#define ERRaccess 4 /* No permissions to do the requested operation */ -#define ERRinvnid 5 /* tid invalid */ -#define ERRinvnetname 6 /* Invalid servername */ -#define ERRinvdevice 7 /* Invalid device */ -#define ERRqfull 49 /* Print queue full */ -#define ERRqtoobig 50 /* Queued item too big */ -#define ERRinvpfid 52 /* Invalid print file in smb_fid */ -#define ERRsmbcmd 64 /* Unrecognised command */ -#define ERRsrverror 65 /* smb server internal error */ -#define ERRfilespecs 67 /* fid and pathname invalid combination */ -#define ERRbadlink 68 /* reserved */ -#define ERRbadpermits 69 /* Access specified for a file is not valid */ -#define ERRbadpid 70 /* reserved */ -#define ERRsetattrmode 71 /* attribute mode invalid */ -#define ERRpaused 81 /* Message server paused */ -#define ERRmsgoff 82 /* Not receiving messages */ -#define ERRnoroom 83 /* No room for message */ -#define ERRrmuns 87 /* too many remote usernames */ -#define ERRtimeout 88 /* operation timed out */ -#define ERRnoresource 89 /* No resources currently available for request. */ -#define ERRtoomanyuids 90 /* too many userids */ -#define ERRbaduid 91 /* bad userid */ -#define ERRuseMPX 250 /* temporarily unable to use raw mode, use MPX mode */ -#define ERRuseSTD 251 /* temporarily unable to use raw mode, use std.mode */ -#define ERRcontMPX 252 /* resume MPX mode */ -#define ERRbadPW /* reserved */ -#define ERRnosupport 0xFFFF - -/* Error codes for the ERRHRD class */ - -#define ERRnowrite 19 /* read only media */ -#define ERRbadunit 20 /* Unknown device */ -#define ERRnotready 21 /* Drive not ready */ -#define ERRbadcmd 22 /* Unknown command */ -#define ERRdata 23 /* Data (CRC) error */ -#define ERRbadreq 24 /* Bad request structure length */ -#define ERRseek 25 -#define ERRbadmedia 26 -#define ERRbadsector 27 -#define ERRnopaper 28 -#define ERRwrite 29 /* write fault */ -#define ERRread 30 /* read fault */ -#define ERRgeneral 31 /* General hardware failure */ -#define ERRwrongdisk 34 -#define ERRFCBunavail 35 -#define ERRsharebufexc 36 /* share buffer exceeded */ -#define ERRdiskfull 39 - -/* - * Access modes when opening a file - */ -#define SMB_ACCMASK 0x0003 -#define SMB_O_RDONLY 0x0000 -#define SMB_O_WRONLY 0x0001 -#define SMB_O_RDWR 0x0002 - -/* offsets into message for common items */ -#define smb_com 8 -#define smb_rcls 9 -#define smb_reh 10 -#define smb_err 11 -#define smb_flg 13 -#define smb_flg2 14 -#define smb_reb 13 -#define smb_tid 28 -#define smb_pid 30 -#define smb_uid 32 -#define smb_mid 34 -#define smb_wct 36 -#define smb_vwv 37 -#define smb_vwv0 37 -#define smb_vwv1 39 -#define smb_vwv2 41 -#define smb_vwv3 43 -#define smb_vwv4 45 -#define smb_vwv5 47 -#define smb_vwv6 49 -#define smb_vwv7 51 -#define smb_vwv8 53 -#define smb_vwv9 55 -#define smb_vwv10 57 -#define smb_vwv11 59 -#define smb_vwv12 61 -#define smb_vwv13 63 -#define smb_vwv14 65 - -/* these are the trans2 sub fields for primary requests */ -#define smb_tpscnt smb_vwv0 -#define smb_tdscnt smb_vwv1 -#define smb_mprcnt smb_vwv2 -#define smb_mdrcnt smb_vwv3 -#define smb_msrcnt smb_vwv4 -#define smb_flags smb_vwv5 -#define smb_timeout smb_vwv6 -#define smb_pscnt smb_vwv9 -#define smb_psoff smb_vwv10 -#define smb_dscnt smb_vwv11 -#define smb_dsoff smb_vwv12 -#define smb_suwcnt smb_vwv13 -#define smb_setup smb_vwv14 -#define smb_setup0 smb_setup -#define smb_setup1 (smb_setup+2) -#define smb_setup2 (smb_setup+4) - -/* these are for the secondary requests */ -#define smb_spscnt smb_vwv2 -#define smb_spsoff smb_vwv3 -#define smb_spsdisp smb_vwv4 -#define smb_sdscnt smb_vwv5 -#define smb_sdsoff smb_vwv6 -#define smb_sdsdisp smb_vwv7 -#define smb_sfid smb_vwv8 - -/* and these for responses */ -#define smb_tprcnt smb_vwv0 -#define smb_tdrcnt smb_vwv1 -#define smb_prcnt smb_vwv3 -#define smb_proff smb_vwv4 -#define smb_prdisp smb_vwv5 -#define smb_drcnt smb_vwv6 -#define smb_droff smb_vwv7 -#define smb_drdisp smb_vwv8 - -/* the complete */ -#define SMBmkdir 0x00 /* create directory */ -#define SMBrmdir 0x01 /* delete directory */ -#define SMBopen 0x02 /* open file */ -#define SMBcreate 0x03 /* create file */ -#define SMBclose 0x04 /* close file */ -#define SMBflush 0x05 /* flush file */ -#define SMBunlink 0x06 /* delete file */ -#define SMBmv 0x07 /* rename file */ -#define SMBgetatr 0x08 /* get file attributes */ -#define SMBsetatr 0x09 /* set file attributes */ -#define SMBread 0x0A /* read from file */ -#define SMBwrite 0x0B /* write to file */ -#define SMBlock 0x0C /* lock byte range */ -#define SMBunlock 0x0D /* unlock byte range */ -#define SMBctemp 0x0E /* create temporary file */ -#define SMBmknew 0x0F /* make new file */ -#define SMBchkpth 0x10 /* check directory path */ -#define SMBexit 0x11 /* process exit */ -#define SMBlseek 0x12 /* seek */ -#define SMBtcon 0x70 /* tree connect */ -#define SMBtconX 0x75 /* tree connect and X*/ -#define SMBtdis 0x71 /* tree disconnect */ -#define SMBnegprot 0x72 /* negotiate protocol */ -#define SMBdskattr 0x80 /* get disk attributes */ -#define SMBsearch 0x81 /* search directory */ -#define SMBsplopen 0xC0 /* open print spool file */ -#define SMBsplwr 0xC1 /* write to print spool file */ -#define SMBsplclose 0xC2 /* close print spool file */ -#define SMBsplretq 0xC3 /* return print queue */ -#define SMBsends 0xD0 /* send single block message */ -#define SMBsendb 0xD1 /* send broadcast message */ -#define SMBfwdname 0xD2 /* forward user name */ -#define SMBcancelf 0xD3 /* cancel forward */ -#define SMBgetmac 0xD4 /* get machine name */ -#define SMBsendstrt 0xD5 /* send start of multi-block message */ -#define SMBsendend 0xD6 /* send end of multi-block message */ -#define SMBsendtxt 0xD7 /* send text of multi-block message */ - -/* Core+ protocol */ -#define SMBlockread 0x13 /* Lock a range and read */ -#define SMBwriteunlock 0x14 /* Unlock a range then write */ -#define SMBreadbraw 0x1a /* read a block of data with no smb header */ -#define SMBwritebraw 0x1d /* write a block of data with no smb header */ -#define SMBwritec 0x20 /* secondary write request */ -#define SMBwriteclose 0x2c /* write a file then close it */ - -/* dos extended protocol */ -#define SMBreadBraw 0x1A /* read block raw */ -#define SMBreadBmpx 0x1B /* read block multiplexed */ -#define SMBreadBs 0x1C /* read block (secondary response) */ -#define SMBwriteBraw 0x1D /* write block raw */ -#define SMBwriteBmpx 0x1E /* write block multiplexed */ -#define SMBwriteBs 0x1F /* write block (secondary request) */ -#define SMBwriteC 0x20 /* write complete response */ -#define SMBsetattrE 0x22 /* set file attributes expanded */ -#define SMBgetattrE 0x23 /* get file attributes expanded */ -#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */ -#define SMBtrans 0x25 /* transaction - name, bytes in/out */ -#define SMBtranss 0x26 /* transaction (secondary request/response) */ -#define SMBioctl 0x27 /* IOCTL */ -#define SMBioctls 0x28 /* IOCTL (secondary request/response) */ -#define SMBcopy 0x29 /* copy */ -#define SMBmove 0x2A /* move */ -#define SMBecho 0x2B /* echo */ -#define SMBopenX 0x2D /* open and X */ -#define SMBreadX 0x2E /* read and X */ -#define SMBwriteX 0x2F /* write and X */ -#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */ -#define SMBtconX 0x75 /* tree connect and X */ -#define SMBffirst 0x82 /* find first */ -#define SMBfunique 0x83 /* find unique */ -#define SMBfclose 0x84 /* find close */ -#define SMBinvalid 0xFE /* invalid command */ - - -/* Extended 2.0 protocol */ -#define SMBtrans2 0x32 /* TRANS2 protocol set */ -#define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */ -#define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */ -#define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */ -#define SMBulogoffX 0x74 /* user logoff */ - -/* these are the TRANS2 sub commands */ -#define TRANSACT2_OPEN 0 -#define TRANSACT2_FINDFIRST 1 -#define TRANSACT2_FINDNEXT 2 -#define TRANSACT2_QFSINFO 3 -#define TRANSACT2_SETFSINFO 4 -#define TRANSACT2_QPATHINFO 5 -#define TRANSACT2_SETPATHINFO 6 -#define TRANSACT2_QFILEINFO 7 -#define TRANSACT2_SETFILEINFO 8 -#define TRANSACT2_FSCTL 9 -#define TRANSACT2_IOCTL 10 -#define TRANSACT2_FINDNOTIFYFIRST 11 -#define TRANSACT2_FINDNOTIFYNEXT 12 -#define TRANSACT2_MKDIR 13 - -/* Information Levels - Shared? */ -#define SMB_INFO_STANDARD 1 -#define SMB_INFO_QUERY_EA_SIZE 2 -#define SMB_INFO_QUERY_EAS_FROM_LIST 3 -#define SMB_INFO_QUERY_ALL_EAS 4 -#define SMB_INFO_IS_NAME_VALID 6 - -/* Information Levels - TRANSACT2_FINDFIRST */ -#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 -#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 -#define SMB_FIND_FILE_NAMES_INFO 0x103 -#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 - -/* Information Levels - TRANSACT2_QPATHINFO */ -#define SMB_QUERY_FILE_BASIC_INFO 0x101 -#define SMB_QUERY_FILE_STANDARD_INFO 0x102 -#define SMB_QUERY_FILE_EA_INFO 0x103 -#define SMB_QUERY_FILE_NAME_INFO 0x104 -#define SMB_QUERY_FILE_ALL_INFO 0x107 -#define SMB_QUERY_FILE_ALT_NAME_INFO 0x108 -#define SMB_QUERY_FILE_STREAM_INFO 0x109 -#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10b - -/* Information Levels - TRANSACT2_SETFILEINFO */ -#define SMB_SET_FILE_BASIC_INFO 0x101 -#define SMB_SET_FILE_DISPOSITION_INFO 0x102 -#define SMB_SET_FILE_ALLOCATION_INFO 0x103 -#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 - -/* smb_flg field flags */ -#define SMB_FLAGS_SUPPORT_LOCKREAD 0x01 -#define SMB_FLAGS_CLIENT_BUF_AVAIL 0x02 -#define SMB_FLAGS_RESERVED 0x04 -#define SMB_FLAGS_CASELESS_PATHNAMES 0x08 -#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10 -#define SMB_FLAGS_REQUEST_OPLOCK 0x20 -#define SMB_FLAGS_REQUEST_BATCH_OPLOCK 0x40 -#define SMB_FLAGS_REPLY 0x80 - -/* smb_flg2 field flags (samba-2.2.0/source/include/smb.h) */ -#define SMB_FLAGS2_LONG_PATH_COMPONENTS 0x0001 -#define SMB_FLAGS2_EXTENDED_ATTRIBUTES 0x0002 -#define SMB_FLAGS2_DFS_PATHNAMES 0x1000 -#define SMB_FLAGS2_READ_PERMIT_NO_EXECUTE 0x2000 -#define SMB_FLAGS2_32_BIT_ERROR_CODES 0x4000 -#define SMB_FLAGS2_UNICODE_STRINGS 0x8000 - - -/* - * UNIX stuff (from samba trans2.h) - */ -#define MIN_UNIX_INFO_LEVEL 0x200 -#define MAX_UNIX_INFO_LEVEL 0x2FF -#define SMB_FIND_FILE_UNIX 0x202 -#define SMB_QUERY_FILE_UNIX_BASIC 0x200 -#define SMB_QUERY_FILE_UNIX_LINK 0x201 -#define SMB_QUERY_FILE_UNIX_HLINK 0x202 -#define SMB_SET_FILE_UNIX_BASIC 0x200 -#define SMB_SET_FILE_UNIX_LINK 0x201 -#define SMB_SET_FILE_UNIX_HLINK 0x203 -#define SMB_QUERY_CIFS_UNIX_INFO 0x200 - -/* values which means "don't change it" */ -#define SMB_MODE_NO_CHANGE 0xFFFFFFFF -#define SMB_UID_NO_CHANGE 0xFFFFFFFF -#define SMB_GID_NO_CHANGE 0xFFFFFFFF -#define SMB_TIME_NO_CHANGE 0xFFFFFFFFFFFFFFFFULL -#define SMB_SIZE_NO_CHANGE 0xFFFFFFFFFFFFFFFFULL - -/* UNIX filetype mappings. */ -#define UNIX_TYPE_FILE 0 -#define UNIX_TYPE_DIR 1 -#define UNIX_TYPE_SYMLINK 2 -#define UNIX_TYPE_CHARDEV 3 -#define UNIX_TYPE_BLKDEV 4 -#define UNIX_TYPE_FIFO 5 -#define UNIX_TYPE_SOCKET 6 -#define UNIX_TYPE_UNKNOWN 0xFFFFFFFF - -#endif /* _SMBNO_H_ */ -- cgit v1.2.3-70-g09d2 From 5793210cb1a431f98c85803920d5ba1440c6246e Mon Sep 17 00:00:00 2001 From: matt mooney Date: Fri, 1 Oct 2010 21:21:55 -0700 Subject: Documentation/kbuild: modules.txt cleanup A few modifications done for consistency, such as adding the shell prompt for command line examples and trailing slash for directories. Also corrects the module include header and fixes a few grammar issues that I introduced. Signed-off-by: matt mooney Signed-off-by: Michal Marek --- Documentation/kbuild/modules.txt | 62 +++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 30 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kbuild/modules.txt b/Documentation/kbuild/modules.txt index b572db3c5fee..3fb39e0116b4 100644 --- a/Documentation/kbuild/modules.txt +++ b/Documentation/kbuild/modules.txt @@ -1,11 +1,11 @@ Building External Modules -This document describes how-to build an out-of-tree kernel module. +This document describes how to build an out-of-tree kernel module. === Table of Contents === 1 Introduction - === 2 How-to Build External Modules + === 2 How to Build External Modules --- 2.1 Command Syntax --- 2.2 Options --- 2.3 Targets @@ -48,9 +48,9 @@ easily accomplished, and a complete example will be presented in section 3. -=== 2. How-to Build External Modules +=== 2. How to Build External Modules -To build external modules, you must have a pre-built kernel available +To build external modules, you must have a prebuilt kernel available that contains the configuration and header files used in the build. Also, the kernel must have been built with modules enabled. If you are using a distribution kernel, there will be a package for the kernel you @@ -69,19 +69,19 @@ executed to make module versioning work. The command to build an external module is: - make -C M=$PWD + $ make -C M=$PWD The kbuild system knows that an external module is being built due to the "M=" option given in the command. To build against the running kernel use: - make -C /lib/modules/`uname -r`/build M=$PWD + $ make -C /lib/modules/`uname -r`/build M=$PWD Then to install the module(s) just built, add the target "modules_install" to the command: - make -C /lib/modules/`uname -r`/build M=$PWD modules_install + $ make -C /lib/modules/`uname -r`/build M=$PWD modules_install --- 2.2 Options @@ -121,7 +121,7 @@ executed to make module versioning work. modules_install Install the external module(s). The default location is - /lib/modules//extra, but a prefix may + /lib/modules//extra/, but a prefix may be added with INSTALL_MOD_PATH (discussed in section 5). clean @@ -164,7 +164,7 @@ needed listing the files: NOTE: Further documentation describing the syntax used by kbuild is located in Documentation/kbuild/makefiles.txt. -The examples below demonstrate how-to create a build file for the +The examples below demonstrate how to create a build file for the module 8123.ko, which is built from the following files: 8123_if.c @@ -205,14 +205,14 @@ module 8123.ko, which is built from the following files: of the makefile. In the example, kbuild will only see the two assignments, whereas "make" will see everything except these two assignments. This is due to two passes made on the file: - the first pass is by the "make" instance run on the - command line; the second pass is by the kbuild system, which is + the first pass is by the "make" instance run on the command + line; the second pass is by the kbuild system, which is initiated by the parameterized "make" in the default target. --- 3.2 Separate Kbuild File and Makefile In newer versions of the kernel, kbuild will first look for a - file named "Kbuild", and only if that is not found, will it + file named "Kbuild," and only if that is not found, will it then look for a makefile. Utilizing a "Kbuild" file allows us to split up the makefile from example 1 into two files: @@ -288,8 +288,8 @@ module 8123.ko, which is built from the following files: --- 3.4 Building Multiple Modules kbuild supports building multiple modules with a single build - file. For example, if you want to build two modules, foo and - bar, the kbuild lines would be: + file. For example, if you wanted to build two modules, foo.ko + and bar.ko, the kbuild lines would be: obj-m := foo.o bar.o foo-y := @@ -320,7 +320,7 @@ according to the following rule: To include a header file located under include/linux/, simply use: - #include + #include kbuild will add options to "gcc" so the relevant directories are searched. @@ -330,7 +330,7 @@ according to the following rule: External modules tend to place header files in a separate include/ directory where their source is located, although this is not the usual kernel style. To inform kbuild of the - directory use either ccflags-y or CFLAGS_.o. + directory, use either ccflags-y or CFLAGS_.o. Using the example from section 3, if we moved 8123_if.h to a subdirectory named include, the resulting kbuild file would @@ -390,11 +390,11 @@ according to the following rule: Modules which are included in the kernel are installed in the directory: - /lib/modules/$(KERNELRELEASE)/kernel + /lib/modules/$(KERNELRELEASE)/kernel/ And external modules are installed in: - /lib/modules/$(KERNELRELEASE)/extra + /lib/modules/$(KERNELRELEASE)/extra/ --- 5.1 INSTALL_MOD_PATH @@ -403,7 +403,7 @@ And external modules are installed in: installation path using the variable INSTALL_MOD_PATH: $ make INSTALL_MOD_PATH=/frodo modules_install - => Install dir: /frodo/lib/modules/$(KERNELRELEASE)/kernel + => Install dir: /frodo/lib/modules/$(KERNELRELEASE)/kernel/ INSTALL_MOD_PATH may be set as an ordinary shell variable or, as shown above, can be specified on the command line when @@ -413,14 +413,14 @@ And external modules are installed in: --- 5.2 INSTALL_MOD_DIR External modules are by default installed to a directory under - /lib/modules/$(KERNELRELEASE)/extra, but you may wish to locate - modules for a specific functionality in a separate directory. - For this purpose, use INSTALL_MOD_DIR to specify an alternative - name to "extra." + /lib/modules/$(KERNELRELEASE)/extra/, but you may wish to + locate modules for a specific functionality in a separate + directory. For this purpose, use INSTALL_MOD_DIR to specify an + alternative name to "extra." $ make INSTALL_MOD_DIR=gandalf -C $KDIR \ M=$PWD modules_install - => Install dir: /lib/modules/$(KERNELRELEASE)/gandalf + => Install dir: /lib/modules/$(KERNELRELEASE)/gandalf/ === 6. Module Versioning @@ -478,9 +478,9 @@ build. Use a top-level kbuild file If you have two modules, foo.ko and bar.ko, where - foo.ko needs symbols from bar.ko, then you can use a + foo.ko needs symbols from bar.ko, you can use a common top-level kbuild file so both modules are - compiled in the same build. Consider following + compiled in the same build. Consider the following directory layout: ./foo/ <= contains foo.ko @@ -491,10 +491,11 @@ build. #./Kbuild (or ./Makefile): obj-y := foo/ bar/ - And executing: + And executing + $ make -C $KDIR M=$PWD - Will then do the expected and compile both modules with + will then do the expected and compile both modules with full knowledge of symbols from either module. Use an extra Module.symvers file @@ -512,10 +513,11 @@ build. Use "make" variable KBUILD_EXTRA_SYMBOLS If it is impractical to copy Module.symvers from another module, you can assign a space separated list - of files to KBUILD_EXTRA_SYMBOLS in your build - file. These files will be loaded by modpost during the + of files to KBUILD_EXTRA_SYMBOLS in your build file. + These files will be loaded by modpost during the initialization of its symbol tables. + === 7. Tips & Tricks --- 7.1 Testing for CONFIG_FOO_BAR -- cgit v1.2.3-70-g09d2 From c2952c314b4fe61820ba8fd6c949eed636140d52 Mon Sep 17 00:00:00 2001 From: Flavio Leitner Date: Tue, 5 Oct 2010 14:23:59 +0000 Subject: bonding: add retransmit membership reports tunable Allow sysadmins to configure the number of multicast membership report sent on a link failure event. Signed-off-by: Flavio Leitner Signed-off-by: David S. Miller --- Documentation/networking/bonding.txt | 8 +++++++ drivers/net/bonding/bond_main.c | 15 ++++++++++++ drivers/net/bonding/bond_sysfs.c | 44 ++++++++++++++++++++++++++++++++++++ drivers/net/bonding/bonding.h | 2 ++ include/linux/if_bonding.h | 3 +++ 5 files changed, 72 insertions(+) (limited to 'Documentation') diff --git a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt index d2b62b71b617..5dc638791d97 100644 --- a/Documentation/networking/bonding.txt +++ b/Documentation/networking/bonding.txt @@ -765,6 +765,14 @@ xmit_hash_policy does not exist, and the layer2 policy is the only policy. The layer2+3 value was added for bonding version 3.2.2. +resend_igmp + + Specifies the number of IGMP membership reports to be issued after + a failover event. One membership report is issued immediately after + the failover, subsequent packets are sent in each 200ms interval. + + The valid range is 0 - 255; the default value is 1. This option + was added for bonding version 3.7.0. 3. Configuring Bonding Devices ============================== diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index ad6386671f28..6f5e6b453da6 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -109,6 +109,7 @@ static char *arp_validate; static char *fail_over_mac; static int all_slaves_active = 0; static struct bond_params bonding_defaults; +static int resend_igmp = BOND_DEFAULT_RESEND_IGMP; module_param(max_bonds, int, 0); MODULE_PARM_DESC(max_bonds, "Max number of bonded devices"); @@ -163,6 +164,8 @@ module_param(all_slaves_active, int, 0); MODULE_PARM_DESC(all_slaves_active, "Keep all frames received on an interface" "by setting active flag for all slaves. " "0 for never (default), 1 for always."); +module_param(resend_igmp, int, 0); +MODULE_PARM_DESC(resend_igmp, "Number of IGMP membership reports to send on link failure"); /*----------------------------- Global variables ----------------------------*/ @@ -905,6 +908,9 @@ static void bond_resend_igmp_join_requests(struct bonding *bond) } } + if (--bond->igmp_retrans > 0) + queue_delayed_work(bond->wq, &bond->mcast_work, HZ/5); + read_unlock(&bond->lock); } @@ -1213,6 +1219,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) * all were sent on curr_active_slave */ if ((USES_PRIMARY(bond->params.mode) && new_active) || bond->params.mode == BOND_MODE_ROUNDROBIN) { + bond->igmp_retrans = bond->params.resend_igmp; queue_delayed_work(bond->wq, &bond->mcast_work, 0); } } @@ -4933,6 +4940,13 @@ static int bond_check_params(struct bond_params *params) all_slaves_active = 0; } + if (resend_igmp < 0 || resend_igmp > 255) { + pr_warning("Warning: resend_igmp (%d) should be between " + "0 and 255, resetting to %d\n", + resend_igmp, BOND_DEFAULT_RESEND_IGMP); + resend_igmp = BOND_DEFAULT_RESEND_IGMP; + } + /* reset values for TLB/ALB */ if ((bond_mode == BOND_MODE_TLB) || (bond_mode == BOND_MODE_ALB)) { @@ -5105,6 +5119,7 @@ static int bond_check_params(struct bond_params *params) params->fail_over_mac = fail_over_mac_value; params->tx_queues = tx_queues; params->all_slaves_active = all_slaves_active; + params->resend_igmp = resend_igmp; if (primary) { strncpy(params->primary, primary, IFNAMSIZ); diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index c311aed9bd02..01b4c3f5d9e7 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -1592,6 +1592,49 @@ out: static DEVICE_ATTR(all_slaves_active, S_IRUGO | S_IWUSR, bonding_show_slaves_active, bonding_store_slaves_active); +/* + * Show and set the number of IGMP membership reports to send on link failure + */ +static ssize_t bonding_show_resend_igmp(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct bonding *bond = to_bond(d); + + return sprintf(buf, "%d\n", bond->params.resend_igmp); +} + +static ssize_t bonding_store_resend_igmp(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int new_value, ret = count; + struct bonding *bond = to_bond(d); + + if (sscanf(buf, "%d", &new_value) != 1) { + pr_err("%s: no resend_igmp value specified.\n", + bond->dev->name); + ret = -EINVAL; + goto out; + } + + if (new_value < 0) { + pr_err("%s: Invalid resend_igmp value %d not in range 0-255; rejected.\n", + bond->dev->name, new_value); + ret = -EINVAL; + goto out; + } + + pr_info("%s: Setting resend_igmp to %d.\n", + bond->dev->name, new_value); + bond->params.resend_igmp = new_value; +out: + return ret; +} + +static DEVICE_ATTR(resend_igmp, S_IRUGO | S_IWUSR, + bonding_show_resend_igmp, bonding_store_resend_igmp); + static struct attribute *per_bond_attrs[] = { &dev_attr_slaves.attr, &dev_attr_mode.attr, @@ -1619,6 +1662,7 @@ static struct attribute *per_bond_attrs[] = { &dev_attr_ad_partner_mac.attr, &dev_attr_queue_id.attr, &dev_attr_all_slaves_active.attr, + &dev_attr_resend_igmp.attr, NULL, }; diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 308ed10dca90..c15f21347486 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -136,6 +136,7 @@ struct bond_params { __be32 arp_targets[BOND_MAX_ARP_TARGETS]; int tx_queues; int all_slaves_active; + int resend_igmp; }; struct bond_parm_tbl { @@ -202,6 +203,7 @@ struct bonding { s8 send_grat_arp; s8 send_unsol_na; s8 setup_by_slave; + s8 igmp_retrans; #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_entry; char proc_file_name[IFNAMSIZ]; diff --git a/include/linux/if_bonding.h b/include/linux/if_bonding.h index 2c7994372bde..a17edda8a781 100644 --- a/include/linux/if_bonding.h +++ b/include/linux/if_bonding.h @@ -84,6 +84,9 @@ #define BOND_DEFAULT_MAX_BONDS 1 /* Default maximum number of devices to support */ #define BOND_DEFAULT_TX_QUEUES 16 /* Default number of tx queues per device */ + +#define BOND_DEFAULT_RESEND_IGMP 1 /* Default number of IGMP membership reports */ + /* hashing types */ #define BOND_XMIT_POLICY_LAYER2 0 /* layer 2 (MAC only), default */ #define BOND_XMIT_POLICY_LAYER34 1 /* layer 3+4 (IP ^ (TCP || UDP)) */ -- cgit v1.2.3-70-g09d2 From 72645eff4b2ad6cf2b016b54f9d6817cca0a621d Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Wed, 6 Oct 2010 07:42:43 -0700 Subject: iwlwifi: schedule to deprecate software scan support Hardware scan is the prefer method for all iwlwifi devices; especially for dual-mode functions. Schedule to deprecate the software scan support in kernel 2.6.40 Signed-off-by: Wey-Yi Guy --- Documentation/feature-removal-schedule.txt | 10 ++++++++++ drivers/net/wireless/iwlwifi/iwl-agn.c | 8 ++++---- drivers/net/wireless/iwlwifi/iwl3945-base.c | 5 +++-- 3 files changed, 17 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index be7030e4dd73..cd89d1e5a6a8 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -563,3 +563,13 @@ Why: superseded by acpi_sleep=nonvs Who: Rafael J. Wysocki ---------------------------- + +What: iwlwifi disable_hw_scan module parameters +When: 2.6.40 +Why: Hareware scan is the prefer method for iwlwifi devices for + scanning operation. Remove software scan support for all the + iwlwifi devices. + +Who: Wey-Yi Guy + +---------------------------- diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index e23c554b73a8..e7dc8b92db66 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -4296,9 +4296,8 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* Disabling hardware scan means that mac80211 will perform scans * "the hard way", rather than using device's scan. */ if (cfg->mod_params->disable_hw_scan) { - if (iwl_debug_level & IWL_DL_INFO) - dev_printk(KERN_DEBUG, &(pdev->dev), - "Disabling hw_scan\n"); + dev_printk(KERN_DEBUG, &(pdev->dev), + "sw scan support is deprecated\n"); iwl_hw_ops.hw_scan = NULL; } @@ -4905,7 +4904,8 @@ module_param_named(fw_restart, iwlagn_mod_params.restart_fw, int, S_IRUGO); MODULE_PARM_DESC(fw_restart, "restart firmware in case of error"); module_param_named( disable_hw_scan, iwlagn_mod_params.disable_hw_scan, int, S_IRUGO); -MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); +MODULE_PARM_DESC(disable_hw_scan, + "disable hardware scanning (default 0) (deprecated)"); module_param_named(ucode_alternative, iwlagn_wanted_ucode_alternative, int, S_IRUGO); diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 116777122a79..8b6aa3d38db0 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -3966,7 +3966,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e * "the hard way", rather than using device's scan. */ if (iwl3945_mod_params.disable_hw_scan) { - IWL_DEBUG_INFO(priv, "Disabling hw_scan\n"); + IWL_ERR(priv, "sw scan support is deprecated\n"); iwl3945_hw_ops.hw_scan = NULL; } @@ -4291,7 +4291,8 @@ MODULE_PARM_DESC(debug, "debug output mask"); #endif module_param_named(disable_hw_scan, iwl3945_mod_params.disable_hw_scan, int, S_IRUGO); -MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); +MODULE_PARM_DESC(disable_hw_scan, + "disable hardware scanning (default 0) (deprecated)"); module_param_named(fw_restart3945, iwl3945_mod_params.restart_fw, int, S_IRUGO); MODULE_PARM_DESC(fw_restart3945, "restart firmware in case of error"); -- cgit v1.2.3-70-g09d2 From 70816e2ca526cfe21ce884ecae057a9c1725ad27 Mon Sep 17 00:00:00 2001 From: Gregory Bean Date: Sat, 28 Aug 2010 10:05:45 -0700 Subject: msm: documentation: add gpiomux documentation. Signed-off-by: Gregory Bean Signed-off-by: Daniel Walker --- Documentation/arm/00-INDEX | 2 + Documentation/arm/msm/gpiomux.txt | 176 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 Documentation/arm/msm/gpiomux.txt (limited to 'Documentation') diff --git a/Documentation/arm/00-INDEX b/Documentation/arm/00-INDEX index 7f5fc3ba9c91..ecf7d04bca26 100644 --- a/Documentation/arm/00-INDEX +++ b/Documentation/arm/00-INDEX @@ -6,6 +6,8 @@ Interrupts - ARM Interrupt subsystem documentation IXP2000 - Release Notes for Linux on Intel's IXP2000 Network Processor +msm + - MSM specific documentation Netwinder - Netwinder specific documentation Porting diff --git a/Documentation/arm/msm/gpiomux.txt b/Documentation/arm/msm/gpiomux.txt new file mode 100644 index 000000000000..67a81620adf6 --- /dev/null +++ b/Documentation/arm/msm/gpiomux.txt @@ -0,0 +1,176 @@ +This document provides an overview of the msm_gpiomux interface, which +is used to provide gpio pin multiplexing and configuration on mach-msm +targets. + +History +======= + +The first-generation API for gpio configuration & multiplexing on msm +is the function gpio_tlmm_config(). This function has a few notable +shortcomings, which led to its deprecation and replacement by gpiomux: + +The 'disable' parameter: Setting the second parameter to +gpio_tlmm_config to GPIO_CFG_DISABLE tells the peripheral +processor in charge of the subsystem to perform a look-up into a +low-power table and apply the low-power/sleep setting for the pin. +As the msm family evolved this became problematic. Not all pins +have sleep settings, not all peripheral processors will accept requests +to apply said sleep settings, and not all msm targets have their gpio +subsystems managed by a peripheral processor. In order to get consistent +behavior on all targets, drivers are forced to ignore this parameter, +rendering it useless. + +The 'direction' flag: for all mux-settings other than raw-gpio (0), +the output-enable bit of a gpio is hard-wired to a known +input (usually VDD or ground). For those settings, the direction flag +is meaningless at best, and deceptive at worst. In addition, using the +direction flag to change output-enable (OE) directly can cause trouble in +gpiolib, which has no visibility into gpio direction changes made +in this way. Direction control in gpio mode should be made through gpiolib. + +Key Features of gpiomux +======================= + +- A consistent interface across all generations of msm. Drivers can expect +the same results on every target. +- gpiomux plays nicely with gpiolib. Functions that should belong to gpiolib +are left to gpiolib and not duplicated here. gpiomux is written with the +intent that gpio_chips will call gpiomux reference-counting methods +from their request() and free() hooks, providing full integration. +- Tabular configuration. Instead of having to call gpio_tlmm_config +hundreds of times, gpio configuration is placed in a single table. +- Per-gpio sleep. Each gpio is individually reference counted, allowing only +those lines which are in use to be put in high-power states. +- 0 means 'do nothing': all flags are designed so that the default memset-zero +equates to a sensible default of 'no configuration', preventing users +from having to provide hundreds of 'no-op' configs for unused or +unwanted lines. + +Usage +===== + +To use gpiomux, provide configuration information for relevant gpio lines +in the msm_gpiomux_configs table. Since a 0 equates to "unconfigured", +only those lines to be managed by gpiomux need to be specified. Here +is a completely fictional example: + +struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS] = { + [12] = { + .active = GPIOMUX_VALID | GPIOMUX_DRV_8MA | GPIOMUX_FUNC_1, + .suspended = GPIOMUX_VALID | GPIOMUX_PULL_DOWN, + }, + [34] = { + .suspended = GPIOMUX_VALID | GPIOMUX_PULL_DOWN, + }, +}; + +To indicate that a gpio is in use, call msm_gpiomux_get() to increase +its reference count. To decrease the reference count, call msm_gpiomux_put(). + +The effect of this configuration is as follows: + +When the system boots, gpios 12 and 34 will be initialized with their +'suspended' configurations. All other gpios, which were left unconfigured, +will not be touched. + +When msm_gpiomux_get() is called on gpio 12 to raise its reference count +above 0, its active configuration will be applied. Since no other gpio +line has a valid active configuration, msm_gpiomux_get() will have no +effect on any other line. + +When msm_gpiomux_put() is called on gpio 12 or 34 to drop their reference +count to 0, their suspended configurations will be applied. +Since no other gpio line has a valid suspended configuration, no other +gpio line will be effected by msm_gpiomux_put(). Since gpio 34 has no valid +active configuration, this is effectively a no-op for gpio 34 as well, +with one small caveat, see the section "About Output-Enable Settings". + +All of the GPIOMUX_VALID flags may seem like unnecessary overhead, but +they address some important issues. As unused entries (all those +except 12 and 34) are zero-filled, gpiomux needs a way to distinguish +the used fields from the unused. In addition, the all-zero pattern +is a valid configuration! Therefore, gpiomux defines an additional bit +which is used to indicate when a field is used. This has the pleasant +side-effect of allowing calls to msm_gpiomux_write to use '0' to indicate +that a value should not be changed: + + msm_gpiomux_write(0, GPIOMUX_VALID, 0); + +replaces the active configuration of gpio 0 with an all-zero configuration, +but leaves the suspended configuration as it was. + +Static Configurations +===================== + +To install a static configuration, which is applied at boot and does +not change after that, install a configuration with a suspended component +but no active component, as in the previous example: + + [34] = { + .suspended = GPIOMUX_VALID | GPIOMUX_PULL_DOWN, + }, + +The suspended setting is applied during boot, and the lack of any valid +active setting prevents any other setting from being applied at runtime. +If other subsystems attempting to access the line is a concern, one could +*really* anchor the configuration down by calling msm_gpiomux_get on the +line at initialization to move the line into active mode. With the line +held, it will never be re-suspended, and with no valid active configuration, +no new configurations will be applied. + +But then, if having other subsystems grabbing for the line is truly a concern, +it should be reserved with gpio_request instead, which carries an implicit +msm_gpiomux_get. + +gpiomux and gpiolib +=================== + +It is expected that msm gpio_chips will call msm_gpiomux_get() and +msm_gpiomux_put() from their request and free hooks, like this fictional +example: + +static int request(struct gpio_chip *chip, unsigned offset) +{ + return msm_gpiomux_get(chip->base + offset); +} + +static void free(struct gpio_chip *chip, unsigned offset) +{ + msm_gpiomux_put(chip->base + offset); +} + + ...somewhere in a gpio_chip declaration... + .request = request, + .free = free, + +This provides important functionality: +- It guarantees that a gpio line will have its 'active' config applied + when the line is requested, and will not be suspended while the line + remains requested; and +- It guarantees that gpio-direction settings from gpiolib behave sensibly. + See "About Output-Enable Settings." + +This mechanism allows for "auto-request" of gpiomux lines via gpiolib +when it is suitable. Drivers wishing more exact control are, of course, +free to also use msm_gpiomux_set and msm_gpiomux_get. + +About Output-Enable Settings +============================ + +Some msm targets do not have the ability to query the current gpio +configuration setting. This means that changes made to the output-enable +(OE) bit by gpiolib cannot be consistently detected and preserved by gpiomux. +Therefore, when gpiomux applies a configuration setting, any direction +settings which may have been applied by gpiolib are lost and the default +input settings are re-applied. + +For this reason, drivers should not assume that gpio direction settings +continue to hold if they free and then re-request a gpio. This seems like +common sense - after all, anybody could have obtained the line in the +meantime - but it needs saying. + +This also means that calls to msm_gpiomux_write will reset the OE bit, +which means that if the gpio line is held by a client of gpiolib and +msm_gpiomux_write is called, the direction setting has been lost and +gpiolib's internal state has been broken. +Release gpio lines before reconfiguring them. -- cgit v1.2.3-70-g09d2 From 955a857e062642cd3ebe1dc7bb38c0f85d8f8f17 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 29 Sep 2010 15:41:49 -0400 Subject: NFS: new idmapper This patch creates a new idmapper system that uses the request-key function to place a call into userspace to map user and group ids to names. The old idmapper was single threaded, which prevented more than one request from running at a single time. This means that a user would have to wait for an upcall to finish before accessing a cached result. The upcall result is stored on a keyring of type id_resolver. See the file Documentation/filesystems/nfs/idmapper.txt for instructions. Signed-off-by: Bryan Schumaker [Trond: fix up the return value of nfs_idmap_lookup_name and clean up code] Signed-off-by: Trond Myklebust --- Documentation/filesystems/nfs/00-INDEX | 2 + Documentation/filesystems/nfs/idmapper.txt | 67 +++++++++ fs/nfs/Kconfig | 11 ++ fs/nfs/idmap.c | 211 ++++++++++++++++++++++++++++- fs/nfs/inode.c | 7 + fs/nfs/nfs4xdr.c | 4 +- fs/nfs/sysctl.c | 2 + include/linux/nfs_idmap.h | 31 ++++- 8 files changed, 329 insertions(+), 6 deletions(-) create mode 100644 Documentation/filesystems/nfs/idmapper.txt (limited to 'Documentation') diff --git a/Documentation/filesystems/nfs/00-INDEX b/Documentation/filesystems/nfs/00-INDEX index 2f68cd688769..3225a5662114 100644 --- a/Documentation/filesystems/nfs/00-INDEX +++ b/Documentation/filesystems/nfs/00-INDEX @@ -14,3 +14,5 @@ nfsroot.txt - short guide on setting up a diskless box with NFS root filesystem. rpc-cache.txt - introduction to the caching mechanisms in the sunrpc layer. +idmapper.txt + - information for configuring request-keys to be used by idmapper diff --git a/Documentation/filesystems/nfs/idmapper.txt b/Documentation/filesystems/nfs/idmapper.txt new file mode 100644 index 000000000000..c3852041a21f --- /dev/null +++ b/Documentation/filesystems/nfs/idmapper.txt @@ -0,0 +1,67 @@ + +========= +ID Mapper +========= +Id mapper is used by NFS to translate user and group ids into names, and to +translate user and group names into ids. Part of this translation involves +performing an upcall to userspace to request the information. Id mapper will +user request-key to perform this upcall and cache the result. The program +/usr/sbin/nfs.upcall should be called by request-key, and will perform the +translation and initialize a key with the resulting information. + + NFS_USE_NEW_IDMAPPER must be selected when configuring the kernel to use this + feature. + +=========== +Configuring +=========== +The file /etc/request-key.conf will need to be modified so /sbin/request-key can +direct the upcall. The following line should be added: + +#OP TYPE DESCRIPTION CALLOUT INFO PROGRAM ARG1 ARG2 ARG3 ... +#====== ======= =============== =============== =============================== +create id_resolver * * /usr/sbin/nfs.upcall %k %d 600 + +This will direct all id_resolver requests to the program /usr/sbin/nfs.upcall. +The last parameter, 600, defines how many seconds into the future the key will +expire. This parameter is optional for /usr/sbin/nfs.upcall. When the timeout +is not specified, nfs.upcall will default to 600 seconds. + +id mapper uses for key descriptions: + uid: Find the UID for the given user + gid: Find the GID for the given group + user: Find the user name for the given UID + group: Find the group name for the given GID + +You can handle any of these individually, rather than using the generic upcall +program. If you would like to use your own program for a uid lookup then you +would edit your request-key.conf so it look similar to this: + +#OP TYPE DESCRIPTION CALLOUT INFO PROGRAM ARG1 ARG2 ARG3 ... +#====== ======= =============== =============== =============================== +create id_resolver uid:* * /some/other/program %k %d 600 +create id_resolver * * /usr/sbin/nfs.upcall %k %d 600 + +Notice that the new line was added above the line for the generic program. +request-key will find the first matching line and corresponding program. In +this case, /some/other/program will handle all uid lookups and +/usr/sbin/nfs.upcall will handle gid, user, and group lookups. + +See for more information about the +request-key function. + + +========== +nfs.upcall +========== +nfs.upcall is designed to be called by request-key, and should not be run "by +hand". This program takes two arguments, a serialized key and a key +description. The serialized key is first converted into a key_serial_t, and +then passed as an argument to keyctl_instantiate (both are part of keyutils.h). + +The actual lookups are performed by functions found in nfsidmap.h. nfs.upcall +determines the correct function to call by looking at the first part of the +description string. For example, a uid lookup description will appear as +"uid:user@domain". + +nfs.upcall will return 0 if the key was instantiated, and non-zero otherwise. diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 6c2aad49d731..3f69752d6f18 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -116,3 +116,14 @@ config NFS_USE_KERNEL_DNS select DNS_RESOLVER select KEYS default y + +config NFS_USE_NEW_IDMAPPER + bool "Use the new idmapper upcall routine" + depends on NFS_V4 && KEYS + help + Say Y here if you want NFS to use the new idmapper upcall functions. + You will need /sbin/request-key (usually provided by the keyutils + package). For details, read + . + + If you are unsure, say N. diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 21a84d45916f..dec47ed8b6b9 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -34,6 +34,212 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifdef CONFIG_NFS_USE_NEW_IDMAPPER + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NFS_UINT_MAXLEN 11 + +const struct cred *id_resolver_cache; + +struct key_type key_type_id_resolver = { + .name = "id_resolver", + .instantiate = user_instantiate, + .match = user_match, + .revoke = user_revoke, + .destroy = user_destroy, + .describe = user_describe, + .read = user_read, +}; + +int nfs_idmap_init(void) +{ + struct cred *cred; + struct key *keyring; + int ret = 0; + + printk(KERN_NOTICE "Registering the %s key type\n", key_type_id_resolver.name); + + cred = prepare_kernel_cred(NULL); + if (!cred) + return -ENOMEM; + + keyring = key_alloc(&key_type_keyring, ".id_resolver", 0, 0, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto failed_put_cred; + } + + ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); + if (ret < 0) + goto failed_put_key; + + ret = register_key_type(&key_type_id_resolver); + if (ret < 0) + goto failed_put_key; + + cred->thread_keyring = keyring; + cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; + id_resolver_cache = cred; + return 0; + +failed_put_key: + key_put(keyring); +failed_put_cred: + put_cred(cred); + return ret; +} + +void nfs_idmap_quit(void) +{ + key_revoke(id_resolver_cache->thread_keyring); + unregister_key_type(&key_type_id_resolver); + put_cred(id_resolver_cache); +} + +/* + * Assemble the description to pass to request_key() + * This function will allocate a new string and update dest to point + * at it. The caller is responsible for freeing dest. + * + * On error 0 is returned. Otherwise, the length of dest is returned. + */ +static ssize_t nfs_idmap_get_desc(const char *name, size_t namelen, + const char *type, size_t typelen, char **desc) +{ + char *cp; + size_t desclen = typelen + namelen + 2; + + *desc = kmalloc(desclen, GFP_KERNEL); + if (!desc) + return -ENOMEM; + + cp = *desc; + memcpy(cp, type, typelen); + cp += typelen; + *cp++ = ':'; + + memcpy(cp, name, namelen); + cp += namelen; + *cp = '\0'; + return desclen; +} + +static ssize_t nfs_idmap_request_key(const char *name, size_t namelen, + const char *type, void *data, size_t data_size) +{ + const struct cred *saved_cred; + struct key *rkey; + char *desc; + struct user_key_payload *payload; + ssize_t ret; + + ret = nfs_idmap_get_desc(name, namelen, type, strlen(type), &desc); + if (ret <= 0) + goto out; + + saved_cred = override_creds(id_resolver_cache); + rkey = request_key(&key_type_id_resolver, desc, ""); + revert_creds(saved_cred); + kfree(desc); + if (IS_ERR(rkey)) { + ret = PTR_ERR(rkey); + goto out; + } + + rcu_read_lock(); + rkey->perm |= KEY_USR_VIEW; + + ret = key_validate(rkey); + if (ret < 0) + goto out_up; + + payload = rcu_dereference(rkey->payload.data); + if (IS_ERR_OR_NULL(payload)) { + ret = PTR_ERR(payload); + goto out_up; + } + + ret = payload->datalen; + if (ret > 0 && ret <= data_size) + memcpy(data, payload->data, ret); + else + ret = -EINVAL; + +out_up: + rcu_read_unlock(); + key_put(rkey); +out: + return ret; +} + + +/* ID -> Name */ +static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf, size_t buflen) +{ + char id_str[NFS_UINT_MAXLEN]; + int id_len; + ssize_t ret; + + id_len = snprintf(id_str, sizeof(id_str), "%u", id); + ret = nfs_idmap_request_key(id_str, id_len, type, buf, buflen); + if (ret < 0) + return -EINVAL; + return ret; +} + +/* Name -> ID */ +static int nfs_idmap_lookup_id(const char *name, size_t namelen, + const char *type, __u32 *id) +{ + char id_str[NFS_UINT_MAXLEN]; + long id_long; + ssize_t data_size; + int ret = 0; + + data_size = nfs_idmap_request_key(name, namelen, type, id_str, NFS_UINT_MAXLEN); + if (data_size <= 0) { + ret = -EINVAL; + } else { + ret = strict_strtol(id_str, 10, &id_long); + *id = (__u32)id_long; + } + return ret; +} + +int nfs_map_name_to_uid(struct nfs_client *clp, const char *name, size_t namelen, __u32 *uid) +{ + return nfs_idmap_lookup_id(name, namelen, "uid", uid); +} + +int nfs_map_group_to_gid(struct nfs_client *clp, const char *name, size_t namelen, __u32 *gid) +{ + return nfs_idmap_lookup_id(name, namelen, "gid", gid); +} + +int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen) +{ + return nfs_idmap_lookup_name(uid, "user", buf, buflen); +} +int nfs_map_gid_to_group(struct nfs_client *clp, __u32 gid, char *buf, size_t buflen) +{ + return nfs_idmap_lookup_name(gid, "group", buf, buflen); +} + +#else /* CONFIG_NFS_USE_IDMAPPER not defined */ + #include #include #include @@ -503,16 +709,17 @@ int nfs_map_group_to_gid(struct nfs_client *clp, const char *name, size_t namele return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid); } -int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf) +int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen) { struct idmap *idmap = clp->cl_idmap; return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); } -int nfs_map_gid_to_group(struct nfs_client *clp, __u32 uid, char *buf) +int nfs_map_gid_to_group(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen) { struct idmap *idmap = clp->cl_idmap; return nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf); } +#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */ diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 18be041abd23..f2d2c801e0af 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1526,6 +1526,10 @@ static int __init init_nfs_fs(void) { int err; + err = nfs_idmap_init(); + if (err < 0) + goto out9; + err = nfs_dns_resolver_init(); if (err < 0) goto out8; @@ -1590,6 +1594,8 @@ out6: out7: nfs_dns_resolver_destroy(); out8: + nfs_idmap_quit(); +out9: return err; } @@ -1602,6 +1608,7 @@ static void __exit exit_nfs_fs(void) nfs_destroy_nfspagecache(); nfs_fscache_unregister(); nfs_dns_resolver_destroy(); + nfs_idmap_quit(); #ifdef CONFIG_PROC_FS rpc_proc_unregister("nfs"); #endif diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 3feace66b981..6ea5c9392fe4 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -816,7 +816,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const if (iap->ia_valid & ATTR_MODE) len += 4; if (iap->ia_valid & ATTR_UID) { - owner_namelen = nfs_map_uid_to_name(server->nfs_client, iap->ia_uid, owner_name); + owner_namelen = nfs_map_uid_to_name(server->nfs_client, iap->ia_uid, owner_name, IDMAP_NAMESZ); if (owner_namelen < 0) { dprintk("nfs: couldn't resolve uid %d to string\n", iap->ia_uid); @@ -828,7 +828,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const len += 4 + (XDR_QUADLEN(owner_namelen) << 2); } if (iap->ia_valid & ATTR_GID) { - owner_grouplen = nfs_map_gid_to_group(server->nfs_client, iap->ia_gid, owner_group); + owner_grouplen = nfs_map_gid_to_group(server->nfs_client, iap->ia_gid, owner_group, IDMAP_NAMESZ); if (owner_grouplen < 0) { dprintk("nfs: couldn't resolve gid %d to string\n", iap->ia_gid); diff --git a/fs/nfs/sysctl.c b/fs/nfs/sysctl.c index ad4d2e787b20..978aaeb8a093 100644 --- a/fs/nfs/sysctl.c +++ b/fs/nfs/sysctl.c @@ -32,6 +32,7 @@ static ctl_table nfs_cb_sysctls[] = { .extra1 = (int *)&nfs_set_port_min, .extra2 = (int *)&nfs_set_port_max, }, +#ifndef CONFIG_NFS_USE_NEW_IDMAPPER { .procname = "idmap_cache_timeout", .data = &nfs_idmap_cache_timeout, @@ -39,6 +40,7 @@ static ctl_table nfs_cb_sysctls[] = { .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, +#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */ #endif { .procname = "nfs_mountpoint_timeout", diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index 91a1c24e0cbf..e8352dc5afb5 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -66,13 +66,40 @@ struct idmap_msg { /* Forward declaration to make this header independent of others */ struct nfs_client; +#ifdef CONFIG_NFS_USE_NEW_IDMAPPER + +int nfs_idmap_init(void); +void nfs_idmap_quit(void); + +static inline int nfs_idmap_new(struct nfs_client *clp) +{ + return 0; +} + +static inline void nfs_idmap_delete(struct nfs_client *clp) +{ +} + +#else /* CONFIG_NFS_USE_NEW_IDMAPPER not set */ + +static inline int nfs_idmap_init(void) +{ + return 0; +} + +static inline void nfs_idmap_quit(void) +{ +} + int nfs_idmap_new(struct nfs_client *); void nfs_idmap_delete(struct nfs_client *); +#endif /* CONFIG_NFS_USE_NEW_IDMAPPER */ + int nfs_map_name_to_uid(struct nfs_client *, const char *, size_t, __u32 *); int nfs_map_group_to_gid(struct nfs_client *, const char *, size_t, __u32 *); -int nfs_map_uid_to_name(struct nfs_client *, __u32, char *); -int nfs_map_gid_to_group(struct nfs_client *, __u32, char *); +int nfs_map_uid_to_name(struct nfs_client *, __u32, char *, size_t); +int nfs_map_gid_to_group(struct nfs_client *, __u32, char *, size_t); extern unsigned int nfs_idmap_cache_timeout; #endif /* __KERNEL__ */ -- cgit v1.2.3-70-g09d2 From 03789f26722a15ccfe6f191e9fb3d356f2f18a1e Mon Sep 17 00:00:00 2001 From: Rémi Denis-Courmont Date: Fri, 8 Oct 2010 04:02:02 +0000 Subject: Phonet: cleanup pipe enable socket option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current code works like this: int garbage, status; socklen_t len = sizeof(status); /* enable pipe */ setsockopt(fd, SOL_PNPIPE, PNPIPE_ENABLE, &garbage, sizeof(garbage)); /* disable pipe */ setsockopt(fd, SOL_PNPIPE, PNPIPE_DISABLE, &garbage, sizeof(garbage)); /* get status */ getsockopt(fd, SOL_PNPIPE, PNPIPE_INQ, &status, &len); ...which does not follow the usual socket option pattern. This patch merges all three "options" into a single gettable&settable option, before Linux 2.6.37 gets out: int status; socklen_t len = sizeof(status); /* enable pipe */ status = 1; setsockopt(fd, SOL_PNPIPE, PNPIPE_ENABLE, &status, sizeof(status)); /* disable pipe */ status = 0; setsockopt(fd, SOL_PNPIPE, PNPIPE_ENABLE, &status, sizeof(status)); /* get status */ getsockopt(fd, SOL_PNPIPE, PNPIPE_ENABLE, &status, &len); This also fixes the error code from EFAULT to ENOTCONN. Signed-off-by: Rémi Denis-Courmont Cc: Kumar Sanghvi Signed-off-by: David S. Miller --- Documentation/networking/phonet.txt | 15 ++------ include/linux/phonet.h | 3 +- net/phonet/pep.c | 72 ++++++++++++++++--------------------- 3 files changed, 34 insertions(+), 56 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/phonet.txt b/Documentation/networking/phonet.txt index cccf5ff07ec2..2d9bc2b711fc 100644 --- a/Documentation/networking/phonet.txt +++ b/Documentation/networking/phonet.txt @@ -213,12 +213,9 @@ The implementation adds socket options at SOL_PNPIPE level: It then updates the pipe state associated with the sequenced socket to be PIPE_DISABLED. - PNPIPE_ENABLE - It follows the same sequence as above for enabling a pipe by sending - PNS_PEP_ENABLE_REQ initially and then sending PNS_PEP_ENABLED_IND after - getting responses from sequenced socket and remote-pep. - It will also update the pipe state associated with the sequenced socket - to PIPE_ENABLED. + PNPIPE_ENABLE accepts one integer value (int). If set to zero, the pipe + is disabled. If the value is non-zero, the pipe is enabled. If the pipe + is not (yet) connected, ENOTCONN is error is returned. PNPIPE_DESTROY This will send out PNS_PEP_DISCONNECT_REQ on the sequenced socket and @@ -226,12 +223,6 @@ The implementation adds socket options at SOL_PNPIPE level: It will also update the pipe state associated with the sequenced socket to PIPE_IDLE - PNPIPE_INQ - This getsocktopt allows the user-space running on the sequenced socket - to examine the pipe state associated with that socket ie. whether the - pipe is created (PIPE_DISABLED) or enabled (PIPE_ENABLED) or disabled - (PIPE_DISABLED) or no pipe exists (PIPE_IDLE). - After a pipe has been created and enabled successfully, the Pipe data can be exchanged between the host-pep and remote-pep (modem). diff --git a/include/linux/phonet.h b/include/linux/phonet.h index 96f5625d62fa..e27cbf931740 100644 --- a/include/linux/phonet.h +++ b/include/linux/phonet.h @@ -38,9 +38,8 @@ #define PNPIPE_IFINDEX 2 #define PNPIPE_CREATE 3 #define PNPIPE_ENABLE 4 -#define PNPIPE_DISABLE 5 +/* unused slot */ #define PNPIPE_DESTROY 6 -#define PNPIPE_INQ 7 #define PNADDR_ANY 0 #define PNADDR_BROADCAST 0xFC diff --git a/net/phonet/pep.c b/net/phonet/pep.c index aa3d8700d213..f818f76d297d 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -327,29 +327,20 @@ static int pipe_handler_send_ind(struct sock *sk, u16 dobj, u8 utid, return pn_skb_send(sk, skb, &spn); } -static int pipe_handler_enable_pipe(struct sock *sk, int cmd) +static int pipe_handler_enable_pipe(struct sock *sk, int enable) { - int ret; struct pep_sock *pn = pep_sk(sk); - - switch (cmd) { - case PNPIPE_ENABLE: - ret = pipe_handler_send_req(sk, pn->pn_sk.sobject, - PNS_PIPE_ENABLE_UTID, PNS_PEP_ENABLE_REQ, - pn->pipe_handle, GFP_ATOMIC); - break; - - case PNPIPE_DISABLE: - ret = pipe_handler_send_req(sk, pn->pn_sk.sobject, - PNS_PIPE_DISABLE_UTID, PNS_PEP_DISABLE_REQ, - pn->pipe_handle, GFP_ATOMIC); - break; - - default: - ret = -EINVAL; + int utid, req; + + if (enable) { + utid = PNS_PIPE_ENABLE_UTID; + req = PNS_PEP_ENABLE_REQ; + } else { + utid = PNS_PIPE_DISABLE_UTID; + req = PNS_PEP_DISABLE_REQ; } - - return ret; + return pipe_handler_send_req(sk, pn->pn_sk.sobject, utid, req, + pn->pipe_handle, GFP_ATOMIC); } static int pipe_handler_create_pipe(struct sock *sk, int pipe_handle, int cmd) @@ -1187,23 +1178,6 @@ static int pep_setsockopt(struct sock *sk, int level, int optname, break; } - case PNPIPE_ENABLE: - if (pn->pipe_state != PIPE_DISABLED) { - err = -EFAULT; - break; - } - err = pipe_handler_enable_pipe(sk, PNPIPE_ENABLE); - break; - - case PNPIPE_DISABLE: - if (pn->pipe_state != PIPE_ENABLED) { - err = -EFAULT; - break; - } - - err = pipe_handler_enable_pipe(sk, PNPIPE_DISABLE); - break; - case PNPIPE_DESTROY: if (pn->pipe_state < PIPE_DISABLED) { err = -EFAULT; @@ -1239,6 +1213,17 @@ static int pep_setsockopt(struct sock *sk, int level, int optname, err = 0; } goto out_norel; + +#ifdef CONFIG_PHONET_PIPECTRLR + case PNPIPE_ENABLE: + if (pn->pipe_state <= PIPE_IDLE) { + err = -ENOTCONN; + break; + } + err = pipe_handler_enable_pipe(sk, val); + break; +#endif + default: err = -ENOPROTOOPT; } @@ -1264,15 +1249,18 @@ static int pep_getsockopt(struct sock *sk, int level, int optname, val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE; break; + case PNPIPE_IFINDEX: + val = pn->ifindex; + break; + #ifdef CONFIG_PHONET_PIPECTRLR - case PNPIPE_INQ: - val = pn->pipe_state; + case PNPIPE_ENABLE: + if (pn->pipe_state <= PIPE_IDLE) + return -ENOTCONN; + val = pn->pipe_state != PIPE_DISABLED; break; #endif - case PNPIPE_IFINDEX: - val = pn->ifindex; - break; default: return -ENOPROTOOPT; } -- cgit v1.2.3-70-g09d2 From 3e51d3c924aea8a1f1372e6c615b0a37b528121d Mon Sep 17 00:00:00 2001 From: Kai Makisara Date: Sat, 9 Oct 2010 00:17:56 +0300 Subject: [SCSI] st: add MTWEOFI to write filemarks without flushing drive buffer This patch adds a new MTIOCTOP operation MTWEOFI that writes filemarks with immediate bit set. This means that the drive does not flush its buffer and the next file can be started immediately. This speeds up writing in applications that have to write multiple small files. Signed-off-by: Kai Makisara Signed-off-by: James Bottomley --- Documentation/scsi/st.txt | 15 ++++++++++++++- drivers/scsi/st.c | 15 +++++++++------ include/linux/mtio.h | 1 + 3 files changed, 24 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/scsi/st.txt b/Documentation/scsi/st.txt index 40752602c050..691ca292c24d 100644 --- a/Documentation/scsi/st.txt +++ b/Documentation/scsi/st.txt @@ -2,7 +2,7 @@ This file contains brief information about the SCSI tape driver. The driver is currently maintained by Kai Mäkisara (email Kai.Makisara@kolumbus.fi) -Last modified: Sun Feb 24 21:59:07 2008 by kai.makisara +Last modified: Sun Aug 29 18:25:47 2010 by kai.makisara BASICS @@ -85,6 +85,17 @@ writing and the last operation has been a write. Two filemarks can be optionally written. In both cases end of data is signified by returning zero bytes for two consecutive reads. +Writing filemarks without the immediate bit set in the SCSI command block acts +as a synchronization point, i.e., all remaining data form the drive buffers is +written to tape before the command returns. This makes sure that write errors +are caught at that point, but this takes time. In some applications, several +consecutive files must be written fast. The MTWEOFI operation can be used to +write the filemarks without flushing the drive buffer. Writing filemark at +close() is always flushing the drive buffers. However, if the previous +operation is MTWEOFI, close() does not write a filemark. This can be used if +the program wants to close/open the tape device between files and wants to +skip waiting. + If rewind, offline, bsf, or seek is done and previous tape operation was write, a filemark is written before moving tape. @@ -301,6 +312,8 @@ MTBSR Space backward over count records. MTFSS Space forward over count setmarks. MTBSS Space backward over count setmarks. MTWEOF Write count filemarks. +MTWEOFI Write count filemarks with immediate bit set (i.e., does not + wait until data is on tape) MTWSM Write count setmarks. MTREW Rewind tape. MTOFFL Set device off line (often rewind plus eject). diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 24211d0efa6d..9e2c3a72ff4d 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -9,7 +9,7 @@ Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky, Michael Schaefer, J"org Weule, and Eric Youngdale. - Copyright 1992 - 2008 Kai Makisara + Copyright 1992 - 2010 Kai Makisara email Kai.Makisara@kolumbus.fi Some small formal changes - aeb, 950809 @@ -17,7 +17,7 @@ Last modified: 18-JAN-1998 Richard Gooch Devfs support */ -static const char *verstr = "20081215"; +static const char *verstr = "20100829"; #include @@ -2696,18 +2696,21 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon } break; case MTWEOF: + case MTWEOFI: case MTWSM: if (STp->write_prot) return (-EACCES); cmd[0] = WRITE_FILEMARKS; if (cmd_in == MTWSM) cmd[1] = 2; + if (cmd_in == MTWEOFI) + cmd[1] |= 1; cmd[2] = (arg >> 16); cmd[3] = (arg >> 8); cmd[4] = arg; timeout = STp->device->request_queue->rq_timeout; DEBC( - if (cmd_in == MTWEOF) + if (cmd_in != MTWSM) printk(ST_DEB_MSG "%s: Writing %d filemarks.\n", name, cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); else @@ -2883,8 +2886,8 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon else if (chg_eof) STps->eof = ST_NOEOF; - if (cmd_in == MTWEOF) - STps->rw = ST_IDLE; + if (cmd_in == MTWEOF || cmd_in == MTWEOFI) + STps->rw = ST_IDLE; /* prevent automatic WEOF at close */ } else { /* SCSI command was not completely successful. Don't return from this block without releasing the SCSI command block! */ struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat; @@ -2901,7 +2904,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon else undone = 0; - if (cmd_in == MTWEOF && + if ((cmd_in == MTWEOF || cmd_in == MTWEOFI) && cmdstatp->have_sense && (cmdstatp->flags & SENSE_EOM)) { if (cmdstatp->sense_hdr.sense_key == NO_SENSE || diff --git a/include/linux/mtio.h b/include/linux/mtio.h index ef01d6aa5934..8f825756c459 100644 --- a/include/linux/mtio.h +++ b/include/linux/mtio.h @@ -63,6 +63,7 @@ struct mtop { #define MTCOMPRESSION 32/* control compression with SCSI mode page 15 */ #define MTSETPART 33 /* Change the active tape partition */ #define MTMKPART 34 /* Format the tape with one or two partitions */ +#define MTWEOFI 35 /* write an end-of-file record (mark) in immediate mode */ /* structure for MTIOCGET - mag tape get status command */ -- cgit v1.2.3-70-g09d2 From 66fa12c571d35e3cd62574c65f1785a460105397 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 10 Oct 2010 00:12:20 +0200 Subject: ieee1394: remove the old IEEE 1394 driver stack The drivers - ohci1394 (controller driver) - ieee1394 (core) - dv1394, raw1394, video1394 (userspace ABI) - eth1394, sbp2 (protocol drivers) are replaced by - firewire-ohci (controller driver) - firewire-core (core and userspace ABI) - firewire-net, firewire-sbp2 (protocol drivers) which are more featureful, better performing, and more secure than the older drivers; all with a smaller and more modern code base. The driver firedtv in drivers/media/dvb/firewire/ contains backends to both ieee1394 and firewire-core. Its ieee1394 backend code can be removed in an independent commit; firedtv as-is builds and works fine without ieee1394. The driver pcilynx (an incomplete controller driver) is deleted without replacement since PCILynx cards are extremely rare. Owners of these cards use them with the stand-alone bus sniffer driver nosy instead. The drivers nosy and init_ohci1394_dma which do not interact with either of the two IEEE 1394 stacks are not affected by the ieee1394 subsystem removal. There are still some issues with the newer firewire subsystem compared to the older one: - The rare and quirky controllers ALi M52xx, Apple UniNorth v1, NVIDIA NForce2 are even less well supported by firewire-ohci than by ohci1394. I am looking into the M52xx issue. - The experimental firewire-net is reportedly less stable than its experimental cousin eth1394. - Audio playback of a certain group of audio devices (ones based on DICE chipset with EAP; supported by prerelease FFADO code) does not work yet. This issue is still under investigation. - There were some ieee1394 based out-of-the-mainline drivers. Of them, only lisight, an audio driver for iSight webcams, seems still useful. Work is underway to reimplement it on top of firewire-core. All these remainig issues are minor; they should not stand in the way of overall better user experience of IEEE 1394 on Linux, together with a reduction in support efforts and maintenance burden. The coexistence of two IEEE 1394 kernel driver stacks in the mainline since 2.6.22 shall end now, as announced earlier this year. Signed-off-by: Stefan Richter --- Documentation/ABI/obsolete/dv1394 | 9 - Documentation/ABI/removed/dv1394 | 14 + Documentation/ABI/removed/raw1394 | 15 + .../ABI/removed/raw1394_legacy_isochronous | 16 - Documentation/ABI/removed/video1394 | 16 + Documentation/feature-removal-schedule.txt | 10 - drivers/Makefile | 1 - drivers/firewire/Kconfig | 5 - drivers/ieee1394/Kconfig | 182 - drivers/ieee1394/Makefile | 16 - drivers/ieee1394/config_roms.c | 194 -- drivers/ieee1394/config_roms.h | 19 - drivers/ieee1394/csr.c | 843 ----- drivers/ieee1394/csr.h | 99 - drivers/ieee1394/csr1212.c | 1467 -------- drivers/ieee1394/csr1212.h | 383 --- drivers/ieee1394/dma.c | 289 -- drivers/ieee1394/dma.h | 89 - drivers/ieee1394/dv1394-private.h | 587 ---- drivers/ieee1394/dv1394.c | 2584 -------------- drivers/ieee1394/dv1394.h | 305 -- drivers/ieee1394/eth1394.c | 1736 ---------- drivers/ieee1394/eth1394.h | 234 -- drivers/ieee1394/highlevel.c | 691 ---- drivers/ieee1394/highlevel.h | 141 - drivers/ieee1394/hosts.c | 249 -- drivers/ieee1394/hosts.h | 201 -- drivers/ieee1394/ieee1394-ioctl.h | 106 - drivers/ieee1394/ieee1394.h | 220 -- drivers/ieee1394/ieee1394_core.c | 1380 -------- drivers/ieee1394/ieee1394_core.h | 172 - drivers/ieee1394/ieee1394_hotplug.h | 19 - drivers/ieee1394/ieee1394_transactions.c | 595 ---- drivers/ieee1394/ieee1394_transactions.h | 40 - drivers/ieee1394/ieee1394_types.h | 69 - drivers/ieee1394/iso.c | 568 ---- drivers/ieee1394/iso.h | 195 -- drivers/ieee1394/nodemgr.c | 1901 ----------- drivers/ieee1394/nodemgr.h | 186 - drivers/ieee1394/ohci1394.c | 3590 -------------------- drivers/ieee1394/ohci1394.h | 453 --- drivers/ieee1394/pcilynx.c | 1554 --------- drivers/ieee1394/pcilynx.h | 468 --- drivers/ieee1394/raw1394-private.h | 81 - drivers/ieee1394/raw1394.c | 3096 ----------------- drivers/ieee1394/raw1394.h | 191 -- drivers/ieee1394/sbp2.c | 2138 ------------ drivers/ieee1394/sbp2.h | 346 -- drivers/ieee1394/video1394.c | 1528 --------- drivers/ieee1394/video1394.h | 67 - 50 files changed, 45 insertions(+), 29313 deletions(-) delete mode 100644 Documentation/ABI/obsolete/dv1394 create mode 100644 Documentation/ABI/removed/dv1394 create mode 100644 Documentation/ABI/removed/raw1394 delete mode 100644 Documentation/ABI/removed/raw1394_legacy_isochronous create mode 100644 Documentation/ABI/removed/video1394 delete mode 100644 drivers/ieee1394/Kconfig delete mode 100644 drivers/ieee1394/Makefile delete mode 100644 drivers/ieee1394/config_roms.c delete mode 100644 drivers/ieee1394/config_roms.h delete mode 100644 drivers/ieee1394/csr.c delete mode 100644 drivers/ieee1394/csr.h delete mode 100644 drivers/ieee1394/csr1212.c delete mode 100644 drivers/ieee1394/csr1212.h delete mode 100644 drivers/ieee1394/dma.c delete mode 100644 drivers/ieee1394/dma.h delete mode 100644 drivers/ieee1394/dv1394-private.h delete mode 100644 drivers/ieee1394/dv1394.c delete mode 100644 drivers/ieee1394/dv1394.h delete mode 100644 drivers/ieee1394/eth1394.c delete mode 100644 drivers/ieee1394/eth1394.h delete mode 100644 drivers/ieee1394/highlevel.c delete mode 100644 drivers/ieee1394/highlevel.h delete mode 100644 drivers/ieee1394/hosts.c delete mode 100644 drivers/ieee1394/hosts.h delete mode 100644 drivers/ieee1394/ieee1394-ioctl.h delete mode 100644 drivers/ieee1394/ieee1394.h delete mode 100644 drivers/ieee1394/ieee1394_core.c delete mode 100644 drivers/ieee1394/ieee1394_core.h delete mode 100644 drivers/ieee1394/ieee1394_hotplug.h delete mode 100644 drivers/ieee1394/ieee1394_transactions.c delete mode 100644 drivers/ieee1394/ieee1394_transactions.h delete mode 100644 drivers/ieee1394/ieee1394_types.h delete mode 100644 drivers/ieee1394/iso.c delete mode 100644 drivers/ieee1394/iso.h delete mode 100644 drivers/ieee1394/nodemgr.c delete mode 100644 drivers/ieee1394/nodemgr.h delete mode 100644 drivers/ieee1394/ohci1394.c delete mode 100644 drivers/ieee1394/ohci1394.h delete mode 100644 drivers/ieee1394/pcilynx.c delete mode 100644 drivers/ieee1394/pcilynx.h delete mode 100644 drivers/ieee1394/raw1394-private.h delete mode 100644 drivers/ieee1394/raw1394.c delete mode 100644 drivers/ieee1394/raw1394.h delete mode 100644 drivers/ieee1394/sbp2.c delete mode 100644 drivers/ieee1394/sbp2.h delete mode 100644 drivers/ieee1394/video1394.c delete mode 100644 drivers/ieee1394/video1394.h (limited to 'Documentation') diff --git a/Documentation/ABI/obsolete/dv1394 b/Documentation/ABI/obsolete/dv1394 deleted file mode 100644 index 2ee36864ca10..000000000000 --- a/Documentation/ABI/obsolete/dv1394 +++ /dev/null @@ -1,9 +0,0 @@ -What: dv1394 (a.k.a. "OHCI-DV I/O support" for FireWire) -Contact: linux1394-devel@lists.sourceforge.net -Description: - New application development should use raw1394 + userspace libraries - instead, notably libiec61883 which is functionally equivalent. - -Users: - ffmpeg/libavformat (used by a variety of media players) - dvgrab v1.x (replaced by dvgrab2 on top of raw1394 and resp. libraries) diff --git a/Documentation/ABI/removed/dv1394 b/Documentation/ABI/removed/dv1394 new file mode 100644 index 000000000000..c2310b6676f4 --- /dev/null +++ b/Documentation/ABI/removed/dv1394 @@ -0,0 +1,14 @@ +What: dv1394 (a.k.a. "OHCI-DV I/O support" for FireWire) +Date: May 2010 (scheduled), finally removed in kernel v2.6.37 +Contact: linux1394-devel@lists.sourceforge.net +Description: + /dev/dv1394/* were character device files, one for each FireWire + controller and for NTSC and PAL respectively, from which DV data + could be received by read() or transmitted by write(). A few + ioctl()s allowed limited control. + This special-purpose interface has been superseded by libraw1394 + + libiec61883 which are functionally equivalent, support HDV, and + transparently work on top of the newer firewire kernel drivers. + +Users: + ffmpeg/libavformat (if configured for DV1394) diff --git a/Documentation/ABI/removed/raw1394 b/Documentation/ABI/removed/raw1394 new file mode 100644 index 000000000000..490aa1efc4ae --- /dev/null +++ b/Documentation/ABI/removed/raw1394 @@ -0,0 +1,15 @@ +What: raw1394 (a.k.a. "Raw IEEE1394 I/O support" for FireWire) +Date: May 2010 (scheduled), finally removed in kernel v2.6.37 +Contact: linux1394-devel@lists.sourceforge.net +Description: + /dev/raw1394 was a character device file that allowed low-level + access to FireWire buses. Its major drawbacks were its inability + to implement sensible device security policies, and its low level + of abstraction that required userspace clients do duplicate much + of the kernel's ieee1394 core functionality. + Replaced by /dev/fw*, i.e. the ABI of + firewire-core. + +Users: + libraw1394 (works with firewire-cdev too, transparent to library ABI + users) diff --git a/Documentation/ABI/removed/raw1394_legacy_isochronous b/Documentation/ABI/removed/raw1394_legacy_isochronous deleted file mode 100644 index 1b629622d883..000000000000 --- a/Documentation/ABI/removed/raw1394_legacy_isochronous +++ /dev/null @@ -1,16 +0,0 @@ -What: legacy isochronous ABI of raw1394 (1st generation iso ABI) -Date: June 2007 (scheduled), removed in kernel v2.6.23 -Contact: linux1394-devel@lists.sourceforge.net -Description: - The two request types RAW1394_REQ_ISO_SEND, RAW1394_REQ_ISO_LISTEN have - been deprecated for quite some time. They are very inefficient as they - come with high interrupt load and several layers of callbacks for each - packet. Because of these deficiencies, the video1394 and dv1394 drivers - and the 3rd-generation isochronous ABI in raw1394 (rawiso) were created. - -Users: - libraw1394 users via the long deprecated API raw1394_iso_write, - raw1394_start_iso_write, raw1394_start_iso_rcv, raw1394_stop_iso_rcv - - libdc1394, which optionally uses these old libraw1394 calls - alternatively to the more efficient video1394 ABI diff --git a/Documentation/ABI/removed/video1394 b/Documentation/ABI/removed/video1394 new file mode 100644 index 000000000000..c39c25aee77b --- /dev/null +++ b/Documentation/ABI/removed/video1394 @@ -0,0 +1,16 @@ +What: video1394 (a.k.a. "OHCI-1394 Video support" for FireWire) +Date: May 2010 (scheduled), finally removed in kernel v2.6.37 +Contact: linux1394-devel@lists.sourceforge.net +Description: + /dev/video1394/* were character device files, one for each FireWire + controller, which were used for isochronous I/O. It was added as an + alternative to raw1394's isochronous I/O functionality which had + performance issues in its first generation. Any video1394 user had + to use raw1394 + libraw1394 too because video1394 did not provide + asynchronous I/O for device discovery and configuration. + Replaced by /dev/fw*, i.e. the ABI of + firewire-core. + +Users: + libdc1394 (works with firewire-cdev too, transparent to library ABI + users) diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 842aa9de84a6..1388eca94bb2 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -530,16 +530,6 @@ Who: Thomas Gleixner ---------------------------- -What: old ieee1394 subsystem (CONFIG_IEEE1394) -When: 2.6.37 -Files: drivers/ieee1394/ except init_ohci1394_dma.c -Why: superseded by drivers/firewire/ (CONFIG_FIREWIRE) which offers more - features, better performance, and better security, all with smaller - and more modern code base -Who: Stefan Richter - ----------------------------- - What: The acpi_sleep=s4_nonvs command line option When: 2.6.37 Files: arch/x86/kernel/acpi/sleep.c diff --git a/drivers/Makefile b/drivers/Makefile index a2aea53a75ed..ecde80a1de4f 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -51,7 +51,6 @@ obj-y += net/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_FUSION) += message/ obj-y += firewire/ -obj-y += ieee1394/ obj-$(CONFIG_UIO) += uio/ obj-y += cdrom/ obj-y += auxdisplay/ diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig index fcf3ea28340b..40a222e19b2d 100644 --- a/drivers/firewire/Kconfig +++ b/drivers/firewire/Kconfig @@ -3,9 +3,6 @@ menu "IEEE 1394 (FireWire) support" # firewire-core does not depend on PCI but is # not useful without PCI controller driver -comment "You can enable one or both FireWire driver stacks." -comment "The newer stack is recommended." - config FIREWIRE tristate "FireWire driver stack" select CRC_ITU_T @@ -64,8 +61,6 @@ config FIREWIRE_NET To compile this driver as a module, say M here: The module will be called firewire-net. -source "drivers/ieee1394/Kconfig" - config FIREWIRE_NOSY tristate "Nosy - a FireWire traffic sniffer for PCILynx cards" depends on PCI diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig deleted file mode 100644 index e02096cf7d95..000000000000 --- a/drivers/ieee1394/Kconfig +++ /dev/null @@ -1,182 +0,0 @@ -config IEEE1394 - tristate "Legacy alternative FireWire driver stack" - depends on PCI || BROKEN - help - IEEE 1394 describes a high performance serial bus, which is also - known as FireWire(tm) or i.Link(tm) and is used for connecting all - sorts of devices (most notably digital video cameras) to your - computer. - - If you have FireWire hardware and want to use it, say Y here. This - is the core support only, you will also need to select a driver for - your IEEE 1394 adapter. - - To compile this driver as a module, say M here: the module will be - called ieee1394. - - NOTE: - ieee1394 is superseded by the newer firewire-core driver. See - http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for - further information on how to switch to the new FireWire drivers. - -config IEEE1394_OHCI1394 - tristate "OHCI-1394 controllers" - depends on PCI && IEEE1394 - help - Enable this driver if you have an IEEE 1394 controller based on the - OHCI-1394 specification. The current driver is only tested with OHCI - chipsets made by Texas Instruments and NEC. Most third-party vendors - use one of these chipsets. It should work with any OHCI-1394 - compliant card, however. - - To compile this driver as a module, say M here: the module will be - called ohci1394. - - NOTE: - ohci1394 is superseded by the newer firewire-ohci driver. See - http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for - further information on how to switch to the new FireWire drivers. - - If you want to install firewire-ohci and ohci1394 together, you - should configure them only as modules and blacklist the driver(s) - which you don't want to have auto-loaded. Add either - - blacklist ohci1394 - blacklist video1394 - blacklist dv1394 - or - blacklist firewire-ohci - - to /etc/modprobe.conf or /etc/modprobe.d/* and update modprobe.conf - depending on your distribution. - -comment "PCILynx controller requires I2C" - depends on IEEE1394 && I2C=n - -config IEEE1394_PCILYNX - tristate "PCILynx controller" - depends on PCI && IEEE1394 && I2C - select I2C_ALGOBIT - help - Say Y here if you have an IEEE-1394 controller with the Texas - Instruments PCILynx chip. Note: this driver is written for revision - 2 of this chip and may not work with revision 0. - - To compile this driver as a module, say M here: the module will be - called pcilynx. - - Only some old and now very rare PCI and CardBus cards and - PowerMacs G3 B&W contain the PCILynx controller. Therefore - almost everybody can say N here. - -comment "SBP-2 support (for storage devices) requires SCSI" - depends on IEEE1394 && SCSI=n - -config IEEE1394_SBP2 - tristate "Storage devices (SBP-2 protocol)" - depends on IEEE1394 && SCSI - help - This option enables you to use SBP-2 devices connected to an IEEE - 1394 bus. SBP-2 devices include storage devices like harddisks and - DVD drives, also some other FireWire devices like scanners. - - You should also enable support for disks, CD-ROMs, etc. in the SCSI - configuration section. - - To compile this driver as a module, say M here: the module will be - called sbp2. - - NOTE: - sbp2 is superseded by the newer firewire-sbp2 driver. See - http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for - further information on how to switch to the new FireWire drivers. - -config IEEE1394_SBP2_PHYS_DMA - bool "Enable replacement for physical DMA in SBP2" - depends on IEEE1394_SBP2 && VIRT_TO_BUS && EXPERIMENTAL - help - This builds sbp2 for use with non-OHCI host adapters which do not - support physical DMA or for when ohci1394 is run with phys_dma=0. - Physical DMA is data movement without assistance of the drivers' - interrupt handlers. This option includes the interrupt handlers - that are required in absence of this hardware feature. - - This option is buggy and currently broken on some architectures. - If unsure, say N. - -config IEEE1394_ETH1394_ROM_ENTRY - depends on IEEE1394 - bool - default n - -config IEEE1394_ETH1394 - tristate "IP networking over 1394 (experimental)" - depends on IEEE1394 && EXPERIMENTAL && INET - select IEEE1394_ETH1394_ROM_ENTRY - help - This driver implements a functional majority of RFC 2734: IPv4 over - 1394. It will provide IP connectivity with implementations of RFC - 2734 found on other operating systems. It will not communicate with - older versions of this driver found in stock kernels prior to 2.6.3. - This driver is still considered experimental. It does not yet support - MCAP, therefore multicast support is significantly limited. - - The module is called eth1394 although it does not emulate Ethernet. - - NOTE: - eth1394 is superseded by the newer firewire-net driver. See - http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for - further information on how to switch to the new FireWire drivers. - -config IEEE1394_RAWIO - tristate "raw1394 userspace interface" - depends on IEEE1394 - help - This option adds support for the raw1394 device file which enables - direct communication of user programs with IEEE 1394 devices - (isochronous and asynchronous). Almost all application programs - which access FireWire require this option. - - To compile this driver as a module, say M here: the module will be - called raw1394. - - NOTE: - raw1394 is superseded by the newer firewire-core driver. See - http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for - further information on how to switch to the new FireWire drivers. - -config IEEE1394_VIDEO1394 - tristate "video1394 userspace interface" - depends on IEEE1394 && IEEE1394_OHCI1394 - help - This option adds support for the video1394 device files which enable - isochronous communication of user programs with IEEE 1394 devices, - especially video capture or export. This interface is used by all - libdc1394 based programs and by several other programs, in addition to - the raw1394 interface. It is generally not required for DV capture. - - To compile this driver as a module, say M here: the module will be - called video1394. - - NOTE: - video1394 is superseded by the newer firewire-core driver. See - http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for - further information on how to switch to the new FireWire drivers. - -config IEEE1394_DV1394 - tristate "dv1394 userspace interface (deprecated)" - depends on IEEE1394 && IEEE1394_OHCI1394 - help - The dv1394 driver is unsupported and may be removed from Linux in a - future release. Its functionality is now provided by either - raw1394 or firewire-core together with libraries such as libiec61883. - -config IEEE1394_VERBOSEDEBUG - bool "Excessive debugging output" - depends on IEEE1394 - help - If you say Y here, you will get very verbose debugging logs from the - ieee1394 drivers, including sent and received packet headers. This - will quickly result in large amounts of data sent to the system log. - - Say Y if you really need the debugging output. Everyone else says N. diff --git a/drivers/ieee1394/Makefile b/drivers/ieee1394/Makefile deleted file mode 100644 index 427b86b87760..000000000000 --- a/drivers/ieee1394/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# -# Makefile for the Linux IEEE 1394 implementation -# - -ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \ - highlevel.o csr.o nodemgr.o dma.o iso.o \ - csr1212.o config_roms.o - -obj-$(CONFIG_IEEE1394) += ieee1394.o -obj-$(CONFIG_IEEE1394_PCILYNX) += pcilynx.o -obj-$(CONFIG_IEEE1394_OHCI1394) += ohci1394.o -obj-$(CONFIG_IEEE1394_VIDEO1394) += video1394.o -obj-$(CONFIG_IEEE1394_RAWIO) += raw1394.o -obj-$(CONFIG_IEEE1394_SBP2) += sbp2.o -obj-$(CONFIG_IEEE1394_DV1394) += dv1394.o -obj-$(CONFIG_IEEE1394_ETH1394) += eth1394.o diff --git a/drivers/ieee1394/config_roms.c b/drivers/ieee1394/config_roms.c deleted file mode 100644 index 1b981207fa76..000000000000 --- a/drivers/ieee1394/config_roms.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * ConfigROM entries - * - * Copyright (C) 2004 Ben Collins - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#include - -#include "csr1212.h" -#include "ieee1394.h" -#include "ieee1394_types.h" -#include "hosts.h" -#include "ieee1394_core.h" -#include "highlevel.h" -#include "csr.h" -#include "config_roms.h" - -struct hpsb_config_rom_entry { - const char *name; - - /* Base initialization, called at module load */ - int (*init)(void); - - /* Cleanup called at module exit */ - void (*cleanup)(void); - - /* The flag added to host->config_roms */ - unsigned int flag; -}; - -/* The default host entry. This must succeed. */ -int hpsb_default_host_entry(struct hpsb_host *host) -{ - struct csr1212_keyval *root; - struct csr1212_keyval *vend_id = NULL; - struct csr1212_keyval *text = NULL; - char csr_name[128]; - int ret; - - sprintf(csr_name, "Linux - %s", host->driver->name); - root = host->csr.rom->root_kv; - - vend_id = csr1212_new_immediate(CSR1212_KV_ID_VENDOR, host->csr.guid_hi >> 8); - text = csr1212_new_string_descriptor_leaf(csr_name); - - if (!vend_id || !text) { - if (vend_id) - csr1212_release_keyval(vend_id); - if (text) - csr1212_release_keyval(text); - csr1212_destroy_csr(host->csr.rom); - return -ENOMEM; - } - - csr1212_associate_keyval(vend_id, text); - csr1212_release_keyval(text); - ret = csr1212_attach_keyval_to_directory(root, vend_id); - csr1212_release_keyval(vend_id); - if (ret != CSR1212_SUCCESS) { - csr1212_destroy_csr(host->csr.rom); - return -ENOMEM; - } - - host->update_config_rom = 1; - - return 0; -} - - -#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY -#include "eth1394.h" - -static struct csr1212_keyval *ip1394_ud; - -static int config_rom_ip1394_init(void) -{ - struct csr1212_keyval *spec_id = NULL; - struct csr1212_keyval *spec_desc = NULL; - struct csr1212_keyval *ver = NULL; - struct csr1212_keyval *ver_desc = NULL; - int ret = -ENOMEM; - - ip1394_ud = csr1212_new_directory(CSR1212_KV_ID_UNIT); - - spec_id = csr1212_new_immediate(CSR1212_KV_ID_SPECIFIER_ID, - ETHER1394_GASP_SPECIFIER_ID); - spec_desc = csr1212_new_string_descriptor_leaf("IANA"); - ver = csr1212_new_immediate(CSR1212_KV_ID_VERSION, - ETHER1394_GASP_VERSION); - ver_desc = csr1212_new_string_descriptor_leaf("IPv4"); - - if (!ip1394_ud || !spec_id || !spec_desc || !ver || !ver_desc) - goto ip1394_fail; - - csr1212_associate_keyval(spec_id, spec_desc); - csr1212_associate_keyval(ver, ver_desc); - if (csr1212_attach_keyval_to_directory(ip1394_ud, spec_id) - == CSR1212_SUCCESS && - csr1212_attach_keyval_to_directory(ip1394_ud, ver) - == CSR1212_SUCCESS) - ret = 0; - -ip1394_fail: - if (ret && ip1394_ud) { - csr1212_release_keyval(ip1394_ud); - ip1394_ud = NULL; - } - - if (spec_id) - csr1212_release_keyval(spec_id); - if (spec_desc) - csr1212_release_keyval(spec_desc); - if (ver) - csr1212_release_keyval(ver); - if (ver_desc) - csr1212_release_keyval(ver_desc); - - return ret; -} - -static void config_rom_ip1394_cleanup(void) -{ - if (ip1394_ud) { - csr1212_release_keyval(ip1394_ud); - ip1394_ud = NULL; - } -} - -int hpsb_config_rom_ip1394_add(struct hpsb_host *host) -{ - if (!ip1394_ud) - return -ENODEV; - - if (csr1212_attach_keyval_to_directory(host->csr.rom->root_kv, - ip1394_ud) != CSR1212_SUCCESS) - return -ENOMEM; - - host->config_roms |= HPSB_CONFIG_ROM_ENTRY_IP1394; - host->update_config_rom = 1; - return 0; -} -EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_add); - -void hpsb_config_rom_ip1394_remove(struct hpsb_host *host) -{ - csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, ip1394_ud); - host->config_roms &= ~HPSB_CONFIG_ROM_ENTRY_IP1394; - host->update_config_rom = 1; -} -EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_remove); - -static struct hpsb_config_rom_entry ip1394_entry = { - .name = "ip1394", - .init = config_rom_ip1394_init, - .cleanup = config_rom_ip1394_cleanup, - .flag = HPSB_CONFIG_ROM_ENTRY_IP1394, -}; - -#endif /* CONFIG_IEEE1394_ETH1394_ROM_ENTRY */ - -static struct hpsb_config_rom_entry *const config_rom_entries[] = { -#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY - &ip1394_entry, -#endif -}; - -/* Initialize all config roms */ -int hpsb_init_config_roms(void) -{ - int i, error = 0; - - for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++) - if (config_rom_entries[i]->init()) { - HPSB_ERR("Failed to initialize config rom entry `%s'", - config_rom_entries[i]->name); - error = -1; - } - - return error; -} - -/* Cleanup all config roms */ -void hpsb_cleanup_config_roms(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++) - config_rom_entries[i]->cleanup(); -} diff --git a/drivers/ieee1394/config_roms.h b/drivers/ieee1394/config_roms.h deleted file mode 100644 index 1f5cd1f16c44..000000000000 --- a/drivers/ieee1394/config_roms.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _IEEE1394_CONFIG_ROMS_H -#define _IEEE1394_CONFIG_ROMS_H - -struct hpsb_host; - -int hpsb_default_host_entry(struct hpsb_host *host); -int hpsb_init_config_roms(void); -void hpsb_cleanup_config_roms(void); - -/* List of flags to check if a host contains a certain extra config rom - * entry. Available in the host->config_roms member. */ -#define HPSB_CONFIG_ROM_ENTRY_IP1394 0x00000001 - -#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY -int hpsb_config_rom_ip1394_add(struct hpsb_host *host); -void hpsb_config_rom_ip1394_remove(struct hpsb_host *host); -#endif - -#endif /* _IEEE1394_CONFIG_ROMS_H */ diff --git a/drivers/ieee1394/csr.c b/drivers/ieee1394/csr.c deleted file mode 100644 index d696f69ebce5..000000000000 --- a/drivers/ieee1394/csr.c +++ /dev/null @@ -1,843 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * CSR implementation, iso/bus manager implementation. - * - * Copyright (C) 1999 Andreas E. Bombe - * 2002 Manfred Weihs - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - * - * - * Contributions: - * - * Manfred Weihs - * configuration ROM manipulation - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "csr1212.h" -#include "ieee1394_types.h" -#include "hosts.h" -#include "ieee1394.h" -#include "highlevel.h" -#include "ieee1394_core.h" - -/* Module Parameters */ -/* this module parameter can be used to disable mapping of the FCP registers */ - -static int fcp = 1; -module_param(fcp, int, 0444); -MODULE_PARM_DESC(fcp, "Map FCP registers (default = 1, disable = 0)."); - -static struct csr1212_keyval *node_cap = NULL; - -static void add_host(struct hpsb_host *host); -static void remove_host(struct hpsb_host *host); -static void host_reset(struct hpsb_host *host); -static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer, - u64 addr, size_t length, u16 fl); -static int write_fcp(struct hpsb_host *host, int nodeid, int dest, - quadlet_t *data, u64 addr, size_t length, u16 flags); -static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf, - u64 addr, size_t length, u16 flags); -static int write_regs(struct hpsb_host *host, int nodeid, int destid, - quadlet_t *data, u64 addr, size_t length, u16 flags); -static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store, - u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 fl); -static int lock64_regs(struct hpsb_host *host, int nodeid, octlet_t * store, - u64 addr, octlet_t data, octlet_t arg, int extcode, u16 fl); -static int read_config_rom(struct hpsb_host *host, int nodeid, quadlet_t *buffer, - u64 addr, size_t length, u16 fl); -static u64 allocate_addr_range(u64 size, u32 alignment, void *__host); -static void release_addr_range(u64 addr, void *__host); - -static struct hpsb_highlevel csr_highlevel = { - .name = "standard registers", - .add_host = add_host, - .remove_host = remove_host, - .host_reset = host_reset, -}; - -static const struct hpsb_address_ops map_ops = { - .read = read_maps, -}; - -static const struct hpsb_address_ops fcp_ops = { - .write = write_fcp, -}; - -static const struct hpsb_address_ops reg_ops = { - .read = read_regs, - .write = write_regs, - .lock = lock_regs, - .lock64 = lock64_regs, -}; - -static const struct hpsb_address_ops config_rom_ops = { - .read = read_config_rom, -}; - -struct csr1212_bus_ops csr_bus_ops = { - .allocate_addr_range = allocate_addr_range, - .release_addr = release_addr_range, -}; - - -static u16 csr_crc16(unsigned *data, int length) -{ - int check=0, i; - int shift, sum, next=0; - - for (i = length; i; i--) { - for (next = check, shift = 28; shift >= 0; shift -= 4 ) { - sum = ((next >> 12) ^ (be32_to_cpu(*data) >> shift)) & 0xf; - next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); - } - check = next & 0xffff; - data++; - } - - return check; -} - -static void host_reset(struct hpsb_host *host) -{ - host->csr.state &= 0x300; - - host->csr.bus_manager_id = 0x3f; - host->csr.bandwidth_available = 4915; - host->csr.channels_available_hi = 0xfffffffe; /* pre-alloc ch 31 per 1394a-2000 */ - host->csr.channels_available_lo = ~0; - host->csr.broadcast_channel = 0x80000000 | 31; - - if (host->is_irm) { - if (host->driver->hw_csr_reg) { - host->driver->hw_csr_reg(host, 2, 0xfffffffe, ~0); - } - } - - host->csr.node_ids = host->node_id << 16; - - if (!host->is_root) { - /* clear cmstr bit */ - host->csr.state &= ~0x100; - } - - be32_add_cpu(&host->csr.topology_map[1], 1); - host->csr.topology_map[2] = cpu_to_be32(host->node_count << 16 - | host->selfid_count); - host->csr.topology_map[0] = - cpu_to_be32((host->selfid_count + 2) << 16 - | csr_crc16(host->csr.topology_map + 1, - host->selfid_count + 2)); - - be32_add_cpu(&host->csr.speed_map[1], 1); - host->csr.speed_map[0] = cpu_to_be32(0x3f1 << 16 - | csr_crc16(host->csr.speed_map+1, - 0x3f1)); -} - -/* - * HI == seconds (bits 0:2) - * LO == fractions of a second in units of 125usec (bits 19:31) - * - * Convert SPLIT_TIMEOUT to jiffies. - * The default and minimum as per 1394a-2000 clause 8.3.2.2.6 is 100ms. - */ -static inline void calculate_expire(struct csr_control *csr) -{ - unsigned int usecs = (csr->split_timeout_hi & 7) * 1000000 + - (csr->split_timeout_lo >> 19) * 125; - - csr->expire = usecs_to_jiffies(usecs > 100000 ? usecs : 100000); - HPSB_VERBOSE("CSR: setting expire to %lu, HZ=%u", csr->expire, HZ); -} - - -static void add_host(struct hpsb_host *host) -{ - struct csr1212_keyval *root; - quadlet_t bus_info[CSR_BUS_INFO_SIZE]; - - hpsb_register_addrspace(&csr_highlevel, host, ®_ops, - CSR_REGISTER_BASE, - CSR_REGISTER_BASE + CSR_CONFIG_ROM); - hpsb_register_addrspace(&csr_highlevel, host, &config_rom_ops, - CSR_REGISTER_BASE + CSR_CONFIG_ROM, - CSR_REGISTER_BASE + CSR_CONFIG_ROM_END); - if (fcp) { - hpsb_register_addrspace(&csr_highlevel, host, &fcp_ops, - CSR_REGISTER_BASE + CSR_FCP_COMMAND, - CSR_REGISTER_BASE + CSR_FCP_END); - } - hpsb_register_addrspace(&csr_highlevel, host, &map_ops, - CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP, - CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP_END); - hpsb_register_addrspace(&csr_highlevel, host, &map_ops, - CSR_REGISTER_BASE + CSR_SPEED_MAP, - CSR_REGISTER_BASE + CSR_SPEED_MAP_END); - - spin_lock_init(&host->csr.lock); - - host->csr.state = 0; - host->csr.node_ids = 0; - host->csr.split_timeout_hi = 0; - host->csr.split_timeout_lo = 800 << 19; - calculate_expire(&host->csr); - host->csr.cycle_time = 0; - host->csr.bus_time = 0; - host->csr.bus_manager_id = 0x3f; - host->csr.bandwidth_available = 4915; - host->csr.channels_available_hi = 0xfffffffe; /* pre-alloc ch 31 per 1394a-2000 */ - host->csr.channels_available_lo = ~0; - host->csr.broadcast_channel = 0x80000000 | 31; - - if (host->is_irm) { - if (host->driver->hw_csr_reg) { - host->driver->hw_csr_reg(host, 2, 0xfffffffe, ~0); - } - } - - if (host->csr.max_rec >= 9) - host->csr.max_rom = 2; - else if (host->csr.max_rec >= 5) - host->csr.max_rom = 1; - else - host->csr.max_rom = 0; - - host->csr.generation = 2; - - bus_info[1] = IEEE1394_BUSID_MAGIC; - bus_info[2] = cpu_to_be32((hpsb_disable_irm ? 0 : 1 << CSR_IRMC_SHIFT) | - (1 << CSR_CMC_SHIFT) | - (1 << CSR_ISC_SHIFT) | - (0 << CSR_BMC_SHIFT) | - (0 << CSR_PMC_SHIFT) | - (host->csr.cyc_clk_acc << CSR_CYC_CLK_ACC_SHIFT) | - (host->csr.max_rec << CSR_MAX_REC_SHIFT) | - (host->csr.max_rom << CSR_MAX_ROM_SHIFT) | - (host->csr.generation << CSR_GENERATION_SHIFT) | - host->csr.lnk_spd); - - bus_info[3] = cpu_to_be32(host->csr.guid_hi); - bus_info[4] = cpu_to_be32(host->csr.guid_lo); - - /* The hardware copy of the bus info block will be set later when a - * bus reset is issued. */ - - csr1212_init_local_csr(host->csr.rom, bus_info, host->csr.max_rom); - - root = host->csr.rom->root_kv; - - if(csr1212_attach_keyval_to_directory(root, node_cap) != CSR1212_SUCCESS) { - HPSB_ERR("Failed to attach Node Capabilities to root directory"); - } - - host->update_config_rom = 1; -} - -static void remove_host(struct hpsb_host *host) -{ - quadlet_t bus_info[CSR_BUS_INFO_SIZE]; - - bus_info[1] = IEEE1394_BUSID_MAGIC; - bus_info[2] = cpu_to_be32((0 << CSR_IRMC_SHIFT) | - (0 << CSR_CMC_SHIFT) | - (0 << CSR_ISC_SHIFT) | - (0 << CSR_BMC_SHIFT) | - (0 << CSR_PMC_SHIFT) | - (host->csr.cyc_clk_acc << CSR_CYC_CLK_ACC_SHIFT) | - (host->csr.max_rec << CSR_MAX_REC_SHIFT) | - (0 << CSR_MAX_ROM_SHIFT) | - (0 << CSR_GENERATION_SHIFT) | - host->csr.lnk_spd); - - bus_info[3] = cpu_to_be32(host->csr.guid_hi); - bus_info[4] = cpu_to_be32(host->csr.guid_lo); - - csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, node_cap); - - csr1212_init_local_csr(host->csr.rom, bus_info, 0); - host->update_config_rom = 1; -} - - -int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom, - size_t buffersize, unsigned char rom_version) -{ - unsigned long flags; - int ret; - - HPSB_NOTICE("hpsb_update_config_rom() is deprecated"); - - spin_lock_irqsave(&host->csr.lock, flags); - if (rom_version != host->csr.generation) - ret = -1; - else if (buffersize > host->csr.rom->cache_head->size) - ret = -2; - else { - /* Just overwrite the generated ConfigROM image with new data, - * it can be regenerated later. */ - memcpy(host->csr.rom->cache_head->data, new_rom, buffersize); - host->csr.rom->cache_head->len = buffersize; - - if (host->driver->set_hw_config_rom) - host->driver->set_hw_config_rom(host, host->csr.rom->bus_info_data); - /* Increment the generation number to keep some sort of sync - * with the newer ConfigROM manipulation method. */ - host->csr.generation++; - if (host->csr.generation > 0xf || host->csr.generation < 2) - host->csr.generation = 2; - ret=0; - } - spin_unlock_irqrestore(&host->csr.lock, flags); - return ret; -} - - -/* Read topology / speed maps and configuration ROM */ -static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer, - u64 addr, size_t length, u16 fl) -{ - unsigned long flags; - int csraddr = addr - CSR_REGISTER_BASE; - const char *src; - - spin_lock_irqsave(&host->csr.lock, flags); - - if (csraddr < CSR_SPEED_MAP) { - src = ((char *)host->csr.topology_map) + csraddr - - CSR_TOPOLOGY_MAP; - } else { - src = ((char *)host->csr.speed_map) + csraddr - CSR_SPEED_MAP; - } - - memcpy(buffer, src, length); - spin_unlock_irqrestore(&host->csr.lock, flags); - return RCODE_COMPLETE; -} - - -#define out if (--length == 0) break - -static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf, - u64 addr, size_t length, u16 flags) -{ - int csraddr = addr - CSR_REGISTER_BASE; - int oldcycle; - quadlet_t ret; - - if ((csraddr | length) & 0x3) - return RCODE_TYPE_ERROR; - - length /= 4; - - switch (csraddr) { - case CSR_STATE_CLEAR: - *(buf++) = cpu_to_be32(host->csr.state); - out; - case CSR_STATE_SET: - *(buf++) = cpu_to_be32(host->csr.state); - out; - case CSR_NODE_IDS: - *(buf++) = cpu_to_be32(host->csr.node_ids); - out; - - case CSR_RESET_START: - return RCODE_TYPE_ERROR; - - /* address gap - handled by default below */ - - case CSR_SPLIT_TIMEOUT_HI: - *(buf++) = cpu_to_be32(host->csr.split_timeout_hi); - out; - case CSR_SPLIT_TIMEOUT_LO: - *(buf++) = cpu_to_be32(host->csr.split_timeout_lo); - out; - - /* address gap */ - return RCODE_ADDRESS_ERROR; - - case CSR_CYCLE_TIME: - oldcycle = host->csr.cycle_time; - host->csr.cycle_time = - host->driver->devctl(host, GET_CYCLE_COUNTER, 0); - - if (oldcycle > host->csr.cycle_time) { - /* cycle time wrapped around */ - host->csr.bus_time += 1 << 7; - } - *(buf++) = cpu_to_be32(host->csr.cycle_time); - out; - case CSR_BUS_TIME: - oldcycle = host->csr.cycle_time; - host->csr.cycle_time = - host->driver->devctl(host, GET_CYCLE_COUNTER, 0); - - if (oldcycle > host->csr.cycle_time) { - /* cycle time wrapped around */ - host->csr.bus_time += (1 << 7); - } - *(buf++) = cpu_to_be32(host->csr.bus_time - | (host->csr.cycle_time >> 25)); - out; - - /* address gap */ - return RCODE_ADDRESS_ERROR; - - case CSR_BUSY_TIMEOUT: - /* not yet implemented */ - return RCODE_ADDRESS_ERROR; - - case CSR_BUS_MANAGER_ID: - if (host->driver->hw_csr_reg) - ret = host->driver->hw_csr_reg(host, 0, 0, 0); - else - ret = host->csr.bus_manager_id; - - *(buf++) = cpu_to_be32(ret); - out; - case CSR_BANDWIDTH_AVAILABLE: - if (host->driver->hw_csr_reg) - ret = host->driver->hw_csr_reg(host, 1, 0, 0); - else - ret = host->csr.bandwidth_available; - - *(buf++) = cpu_to_be32(ret); - out; - case CSR_CHANNELS_AVAILABLE_HI: - if (host->driver->hw_csr_reg) - ret = host->driver->hw_csr_reg(host, 2, 0, 0); - else - ret = host->csr.channels_available_hi; - - *(buf++) = cpu_to_be32(ret); - out; - case CSR_CHANNELS_AVAILABLE_LO: - if (host->driver->hw_csr_reg) - ret = host->driver->hw_csr_reg(host, 3, 0, 0); - else - ret = host->csr.channels_available_lo; - - *(buf++) = cpu_to_be32(ret); - out; - - case CSR_BROADCAST_CHANNEL: - *(buf++) = cpu_to_be32(host->csr.broadcast_channel); - out; - - /* address gap to end - fall through to default */ - default: - return RCODE_ADDRESS_ERROR; - } - - return RCODE_COMPLETE; -} - -static int write_regs(struct hpsb_host *host, int nodeid, int destid, - quadlet_t *data, u64 addr, size_t length, u16 flags) -{ - int csraddr = addr - CSR_REGISTER_BASE; - - if ((csraddr | length) & 0x3) - return RCODE_TYPE_ERROR; - - length /= 4; - - switch (csraddr) { - case CSR_STATE_CLEAR: - /* FIXME FIXME FIXME */ - printk("doh, someone wants to mess with state clear\n"); - out; - case CSR_STATE_SET: - printk("doh, someone wants to mess with state set\n"); - out; - - case CSR_NODE_IDS: - host->csr.node_ids &= NODE_MASK << 16; - host->csr.node_ids |= be32_to_cpu(*(data++)) & (BUS_MASK << 16); - host->node_id = host->csr.node_ids >> 16; - host->driver->devctl(host, SET_BUS_ID, host->node_id >> 6); - out; - - case CSR_RESET_START: - /* FIXME - perform command reset */ - out; - - /* address gap */ - return RCODE_ADDRESS_ERROR; - - case CSR_SPLIT_TIMEOUT_HI: - host->csr.split_timeout_hi = - be32_to_cpu(*(data++)) & 0x00000007; - calculate_expire(&host->csr); - out; - case CSR_SPLIT_TIMEOUT_LO: - host->csr.split_timeout_lo = - be32_to_cpu(*(data++)) & 0xfff80000; - calculate_expire(&host->csr); - out; - - /* address gap */ - return RCODE_ADDRESS_ERROR; - - case CSR_CYCLE_TIME: - /* should only be set by cycle start packet, automatically */ - host->csr.cycle_time = be32_to_cpu(*data); - host->driver->devctl(host, SET_CYCLE_COUNTER, - be32_to_cpu(*(data++))); - out; - case CSR_BUS_TIME: - host->csr.bus_time = be32_to_cpu(*(data++)) & 0xffffff80; - out; - - /* address gap */ - return RCODE_ADDRESS_ERROR; - - case CSR_BUSY_TIMEOUT: - /* not yet implemented */ - return RCODE_ADDRESS_ERROR; - - case CSR_BUS_MANAGER_ID: - case CSR_BANDWIDTH_AVAILABLE: - case CSR_CHANNELS_AVAILABLE_HI: - case CSR_CHANNELS_AVAILABLE_LO: - /* these are not writable, only lockable */ - return RCODE_TYPE_ERROR; - - case CSR_BROADCAST_CHANNEL: - /* only the valid bit can be written */ - host->csr.broadcast_channel = (host->csr.broadcast_channel & ~0x40000000) - | (be32_to_cpu(*data) & 0x40000000); - out; - - /* address gap to end - fall through */ - default: - return RCODE_ADDRESS_ERROR; - } - - return RCODE_COMPLETE; -} - -#undef out - - -static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store, - u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 fl) -{ - int csraddr = addr - CSR_REGISTER_BASE; - unsigned long flags; - quadlet_t *regptr = NULL; - - if (csraddr & 0x3) - return RCODE_TYPE_ERROR; - - if (csraddr < CSR_BUS_MANAGER_ID || csraddr > CSR_CHANNELS_AVAILABLE_LO - || extcode != EXTCODE_COMPARE_SWAP) - goto unsupported_lockreq; - - data = be32_to_cpu(data); - arg = be32_to_cpu(arg); - - /* Is somebody releasing the broadcast_channel on us? */ - if (csraddr == CSR_CHANNELS_AVAILABLE_HI && (data & 0x1)) { - /* Note: this is may not be the right way to handle - * the problem, so we should look into the proper way - * eventually. */ - HPSB_WARN("Node [" NODE_BUS_FMT "] wants to release " - "broadcast channel 31. Ignoring.", - NODE_BUS_ARGS(host, nodeid)); - - data &= ~0x1; /* keep broadcast channel allocated */ - } - - if (host->driver->hw_csr_reg) { - quadlet_t old; - - old = host->driver-> - hw_csr_reg(host, (csraddr - CSR_BUS_MANAGER_ID) >> 2, - data, arg); - - *store = cpu_to_be32(old); - return RCODE_COMPLETE; - } - - spin_lock_irqsave(&host->csr.lock, flags); - - switch (csraddr) { - case CSR_BUS_MANAGER_ID: - regptr = &host->csr.bus_manager_id; - *store = cpu_to_be32(*regptr); - if (*regptr == arg) - *regptr = data; - break; - - case CSR_BANDWIDTH_AVAILABLE: - { - quadlet_t bandwidth; - quadlet_t old; - quadlet_t new; - - regptr = &host->csr.bandwidth_available; - old = *regptr; - - /* bandwidth available algorithm adapted from IEEE 1394a-2000 spec */ - if (arg > 0x1fff) { - *store = cpu_to_be32(old); /* change nothing */ - break; - } - data &= 0x1fff; - if (arg >= data) { - /* allocate bandwidth */ - bandwidth = arg - data; - if (old >= bandwidth) { - new = old - bandwidth; - *store = cpu_to_be32(arg); - *regptr = new; - } else { - *store = cpu_to_be32(old); - } - } else { - /* deallocate bandwidth */ - bandwidth = data - arg; - if (old + bandwidth < 0x2000) { - new = old + bandwidth; - *store = cpu_to_be32(arg); - *regptr = new; - } else { - *store = cpu_to_be32(old); - } - } - break; - } - - case CSR_CHANNELS_AVAILABLE_HI: - { - /* Lock algorithm for CHANNELS_AVAILABLE as recommended by 1394a-2000 */ - quadlet_t affected_channels = arg ^ data; - - regptr = &host->csr.channels_available_hi; - - if ((arg & affected_channels) == (*regptr & affected_channels)) { - *regptr ^= affected_channels; - *store = cpu_to_be32(arg); - } else { - *store = cpu_to_be32(*regptr); - } - - break; - } - - case CSR_CHANNELS_AVAILABLE_LO: - { - /* Lock algorithm for CHANNELS_AVAILABLE as recommended by 1394a-2000 */ - quadlet_t affected_channels = arg ^ data; - - regptr = &host->csr.channels_available_lo; - - if ((arg & affected_channels) == (*regptr & affected_channels)) { - *regptr ^= affected_channels; - *store = cpu_to_be32(arg); - } else { - *store = cpu_to_be32(*regptr); - } - break; - } - } - - spin_unlock_irqrestore(&host->csr.lock, flags); - - return RCODE_COMPLETE; - - unsupported_lockreq: - switch (csraddr) { - case CSR_STATE_CLEAR: - case CSR_STATE_SET: - case CSR_RESET_START: - case CSR_NODE_IDS: - case CSR_SPLIT_TIMEOUT_HI: - case CSR_SPLIT_TIMEOUT_LO: - case CSR_CYCLE_TIME: - case CSR_BUS_TIME: - case CSR_BROADCAST_CHANNEL: - return RCODE_TYPE_ERROR; - - case CSR_BUSY_TIMEOUT: - /* not yet implemented - fall through */ - default: - return RCODE_ADDRESS_ERROR; - } -} - -static int lock64_regs(struct hpsb_host *host, int nodeid, octlet_t * store, - u64 addr, octlet_t data, octlet_t arg, int extcode, u16 fl) -{ - int csraddr = addr - CSR_REGISTER_BASE; - unsigned long flags; - - data = be64_to_cpu(data); - arg = be64_to_cpu(arg); - - if (csraddr & 0x3) - return RCODE_TYPE_ERROR; - - if (csraddr != CSR_CHANNELS_AVAILABLE - || extcode != EXTCODE_COMPARE_SWAP) - goto unsupported_lock64req; - - /* Is somebody releasing the broadcast_channel on us? */ - if (csraddr == CSR_CHANNELS_AVAILABLE_HI && (data & 0x100000000ULL)) { - /* Note: this is may not be the right way to handle - * the problem, so we should look into the proper way - * eventually. */ - HPSB_WARN("Node [" NODE_BUS_FMT "] wants to release " - "broadcast channel 31. Ignoring.", - NODE_BUS_ARGS(host, nodeid)); - - data &= ~0x100000000ULL; /* keep broadcast channel allocated */ - } - - if (host->driver->hw_csr_reg) { - quadlet_t data_hi, data_lo; - quadlet_t arg_hi, arg_lo; - quadlet_t old_hi, old_lo; - - data_hi = data >> 32; - data_lo = data & 0xFFFFFFFF; - arg_hi = arg >> 32; - arg_lo = arg & 0xFFFFFFFF; - - old_hi = host->driver->hw_csr_reg(host, (csraddr - CSR_BUS_MANAGER_ID) >> 2, - data_hi, arg_hi); - - old_lo = host->driver->hw_csr_reg(host, ((csraddr + 4) - CSR_BUS_MANAGER_ID) >> 2, - data_lo, arg_lo); - - *store = cpu_to_be64(((octlet_t)old_hi << 32) | old_lo); - } else { - octlet_t old; - octlet_t affected_channels = arg ^ data; - - spin_lock_irqsave(&host->csr.lock, flags); - - old = ((octlet_t)host->csr.channels_available_hi << 32) | host->csr.channels_available_lo; - - if ((arg & affected_channels) == (old & affected_channels)) { - host->csr.channels_available_hi ^= (affected_channels >> 32); - host->csr.channels_available_lo ^= (affected_channels & 0xffffffff); - *store = cpu_to_be64(arg); - } else { - *store = cpu_to_be64(old); - } - - spin_unlock_irqrestore(&host->csr.lock, flags); - } - - /* Is somebody erroneously releasing the broadcast_channel on us? */ - if (host->csr.channels_available_hi & 0x1) - host->csr.channels_available_hi &= ~0x1; - - return RCODE_COMPLETE; - - unsupported_lock64req: - switch (csraddr) { - case CSR_STATE_CLEAR: - case CSR_STATE_SET: - case CSR_RESET_START: - case CSR_NODE_IDS: - case CSR_SPLIT_TIMEOUT_HI: - case CSR_SPLIT_TIMEOUT_LO: - case CSR_CYCLE_TIME: - case CSR_BUS_TIME: - case CSR_BUS_MANAGER_ID: - case CSR_BROADCAST_CHANNEL: - case CSR_BUSY_TIMEOUT: - case CSR_BANDWIDTH_AVAILABLE: - return RCODE_TYPE_ERROR; - - default: - return RCODE_ADDRESS_ERROR; - } -} - -static int write_fcp(struct hpsb_host *host, int nodeid, int dest, - quadlet_t *data, u64 addr, size_t length, u16 flags) -{ - int csraddr = addr - CSR_REGISTER_BASE; - - if (length > 512) - return RCODE_TYPE_ERROR; - - switch (csraddr) { - case CSR_FCP_COMMAND: - highlevel_fcp_request(host, nodeid, 0, (u8 *)data, length); - break; - case CSR_FCP_RESPONSE: - highlevel_fcp_request(host, nodeid, 1, (u8 *)data, length); - break; - default: - return RCODE_TYPE_ERROR; - } - - return RCODE_COMPLETE; -} - -static int read_config_rom(struct hpsb_host *host, int nodeid, quadlet_t *buffer, - u64 addr, size_t length, u16 fl) -{ - u32 offset = addr - CSR1212_REGISTER_SPACE_BASE; - - if (csr1212_read(host->csr.rom, offset, buffer, length) == CSR1212_SUCCESS) - return RCODE_COMPLETE; - else - return RCODE_ADDRESS_ERROR; -} - -static u64 allocate_addr_range(u64 size, u32 alignment, void *__host) -{ - struct hpsb_host *host = (struct hpsb_host*)__host; - - return hpsb_allocate_and_register_addrspace(&csr_highlevel, - host, - &config_rom_ops, - size, alignment, - CSR1212_UNITS_SPACE_BASE, - CSR1212_UNITS_SPACE_END); -} - -static void release_addr_range(u64 addr, void *__host) -{ - struct hpsb_host *host = (struct hpsb_host*)__host; - hpsb_unregister_addrspace(&csr_highlevel, host, addr); -} - - -int init_csr(void) -{ - node_cap = csr1212_new_immediate(CSR1212_KV_ID_NODE_CAPABILITIES, 0x0083c0); - if (!node_cap) { - HPSB_ERR("Failed to allocate memory for Node Capabilties ConfigROM entry!"); - return -ENOMEM; - } - - hpsb_register_highlevel(&csr_highlevel); - - return 0; -} - -void cleanup_csr(void) -{ - if (node_cap) - csr1212_release_keyval(node_cap); - hpsb_unregister_highlevel(&csr_highlevel); -} diff --git a/drivers/ieee1394/csr.h b/drivers/ieee1394/csr.h deleted file mode 100644 index 90fb3f2192c3..000000000000 --- a/drivers/ieee1394/csr.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef _IEEE1394_CSR_H -#define _IEEE1394_CSR_H - -#include - -#include "csr1212.h" -#include "ieee1394_types.h" - -#define CSR_REGISTER_BASE 0xfffff0000000ULL - -/* register offsets relative to CSR_REGISTER_BASE */ -#define CSR_STATE_CLEAR 0x0 -#define CSR_STATE_SET 0x4 -#define CSR_NODE_IDS 0x8 -#define CSR_RESET_START 0xc -#define CSR_SPLIT_TIMEOUT_HI 0x18 -#define CSR_SPLIT_TIMEOUT_LO 0x1c -#define CSR_CYCLE_TIME 0x200 -#define CSR_BUS_TIME 0x204 -#define CSR_BUSY_TIMEOUT 0x210 -#define CSR_BUS_MANAGER_ID 0x21c -#define CSR_BANDWIDTH_AVAILABLE 0x220 -#define CSR_CHANNELS_AVAILABLE 0x224 -#define CSR_CHANNELS_AVAILABLE_HI 0x224 -#define CSR_CHANNELS_AVAILABLE_LO 0x228 -#define CSR_BROADCAST_CHANNEL 0x234 -#define CSR_CONFIG_ROM 0x400 -#define CSR_CONFIG_ROM_END 0x800 -#define CSR_FCP_COMMAND 0xB00 -#define CSR_FCP_RESPONSE 0xD00 -#define CSR_FCP_END 0xF00 -#define CSR_TOPOLOGY_MAP 0x1000 -#define CSR_TOPOLOGY_MAP_END 0x1400 -#define CSR_SPEED_MAP 0x2000 -#define CSR_SPEED_MAP_END 0x3000 - -/* IEEE 1394 bus specific Configuration ROM Key IDs */ -#define IEEE1394_KV_ID_POWER_REQUIREMENTS (0x30) - -/* IEEE 1394 Bus Information Block specifics */ -#define CSR_BUS_INFO_SIZE (5 * sizeof(quadlet_t)) - -#define CSR_IRMC_SHIFT 31 -#define CSR_CMC_SHIFT 30 -#define CSR_ISC_SHIFT 29 -#define CSR_BMC_SHIFT 28 -#define CSR_PMC_SHIFT 27 -#define CSR_CYC_CLK_ACC_SHIFT 16 -#define CSR_MAX_REC_SHIFT 12 -#define CSR_MAX_ROM_SHIFT 8 -#define CSR_GENERATION_SHIFT 4 - -static inline void csr_set_bus_info_generation(struct csr1212_csr *csr, u8 gen) -{ - csr->bus_info_data[2] &= ~cpu_to_be32(0xf << CSR_GENERATION_SHIFT); - csr->bus_info_data[2] |= cpu_to_be32((u32)gen << CSR_GENERATION_SHIFT); -} - -struct csr_control { - spinlock_t lock; - - quadlet_t state; - quadlet_t node_ids; - quadlet_t split_timeout_hi, split_timeout_lo; - unsigned long expire; /* Calculated from split_timeout */ - quadlet_t cycle_time; - quadlet_t bus_time; - quadlet_t bus_manager_id; - quadlet_t bandwidth_available; - quadlet_t channels_available_hi, channels_available_lo; - quadlet_t broadcast_channel; - - /* Bus Info */ - quadlet_t guid_hi, guid_lo; - u8 cyc_clk_acc; - u8 max_rec; - u8 max_rom; - u8 generation; /* Only use values between 0x2 and 0xf */ - u8 lnk_spd; - - unsigned long gen_timestamp[16]; - - struct csr1212_csr *rom; - - quadlet_t topology_map[256]; - quadlet_t speed_map[1024]; -}; - -extern struct csr1212_bus_ops csr_bus_ops; - -int init_csr(void); -void cleanup_csr(void); - -/* hpsb_update_config_rom() is deprecated */ -struct hpsb_host; -int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom, - size_t size, unsigned char rom_version); - -#endif /* _IEEE1394_CSR_H */ diff --git a/drivers/ieee1394/csr1212.c b/drivers/ieee1394/csr1212.c deleted file mode 100644 index e76cac64c533..000000000000 --- a/drivers/ieee1394/csr1212.c +++ /dev/null @@ -1,1467 +0,0 @@ -/* - * csr1212.c -- IEEE 1212 Control and Status Register support for Linux - * - * Copyright (C) 2003 Francois Retief - * Steve Kinneberg - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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. - */ - - -/* TODO List: - * - Verify interface consistency: i.e., public functions that take a size - * parameter expect size to be in bytes. - */ - -#include -#include -#include -#include -#include -#include - -#include "csr1212.h" - - -/* Permitted key type for each key id */ -#define __I (1 << CSR1212_KV_TYPE_IMMEDIATE) -#define __C (1 << CSR1212_KV_TYPE_CSR_OFFSET) -#define __D (1 << CSR1212_KV_TYPE_DIRECTORY) -#define __L (1 << CSR1212_KV_TYPE_LEAF) -static const u8 csr1212_key_id_type_map[0x30] = { - __C, /* used by Apple iSight */ - __D | __L, /* Descriptor */ - __I | __D | __L, /* Bus_Dependent_Info */ - __I | __D | __L, /* Vendor */ - __I, /* Hardware_Version */ - 0, 0, /* Reserved */ - __D | __L | __I, /* Module */ - __I, 0, 0, 0, /* used by Apple iSight, Reserved */ - __I, /* Node_Capabilities */ - __L, /* EUI_64 */ - 0, 0, 0, /* Reserved */ - __D, /* Unit */ - __I, /* Specifier_ID */ - __I, /* Version */ - __I | __C | __D | __L, /* Dependent_Info */ - __L, /* Unit_Location */ - 0, /* Reserved */ - __I, /* Model */ - __D, /* Instance */ - __L, /* Keyword */ - __D, /* Feature */ - __L, /* Extended_ROM */ - __I, /* Extended_Key_Specifier_ID */ - __I, /* Extended_Key */ - __I | __C | __D | __L, /* Extended_Data */ - __L, /* Modifiable_Descriptor */ - __I, /* Directory_ID */ - __I, /* Revision */ -}; -#undef __I -#undef __C -#undef __D -#undef __L - - -#define quads_to_bytes(_q) ((_q) * sizeof(u32)) -#define bytes_to_quads(_b) DIV_ROUND_UP(_b, sizeof(u32)) - -static void free_keyval(struct csr1212_keyval *kv) -{ - if ((kv->key.type == CSR1212_KV_TYPE_LEAF) && - (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)) - CSR1212_FREE(kv->value.leaf.data); - - CSR1212_FREE(kv); -} - -static u16 csr1212_crc16(const u32 *buffer, size_t length) -{ - int shift; - u32 data; - u16 sum, crc = 0; - - for (; length; length--) { - data = be32_to_cpu(*buffer); - buffer++; - for (shift = 28; shift >= 0; shift -= 4 ) { - sum = ((crc >> 12) ^ (data >> shift)) & 0xf; - crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); - } - crc &= 0xffff; - } - - return cpu_to_be16(crc); -} - -/* Microsoft computes the CRC with the bytes in reverse order. */ -static u16 csr1212_msft_crc16(const u32 *buffer, size_t length) -{ - int shift; - u32 data; - u16 sum, crc = 0; - - for (; length; length--) { - data = le32_to_cpu(*buffer); - buffer++; - for (shift = 28; shift >= 0; shift -= 4 ) { - sum = ((crc >> 12) ^ (data >> shift)) & 0xf; - crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); - } - crc &= 0xffff; - } - - return cpu_to_be16(crc); -} - -static struct csr1212_dentry * -csr1212_find_keyval(struct csr1212_keyval *dir, struct csr1212_keyval *kv) -{ - struct csr1212_dentry *pos; - - for (pos = dir->value.directory.dentries_head; - pos != NULL; pos = pos->next) - if (pos->kv == kv) - return pos; - return NULL; -} - -static struct csr1212_keyval * -csr1212_find_keyval_offset(struct csr1212_keyval *kv_list, u32 offset) -{ - struct csr1212_keyval *kv; - - for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next) - if (kv->offset == offset) - return kv; - return NULL; -} - - -/* Creation Routines */ - -struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops, - size_t bus_info_size, void *private) -{ - struct csr1212_csr *csr; - - csr = CSR1212_MALLOC(sizeof(*csr)); - if (!csr) - return NULL; - - csr->cache_head = - csr1212_rom_cache_malloc(CSR1212_CONFIG_ROM_SPACE_OFFSET, - CSR1212_CONFIG_ROM_SPACE_SIZE); - if (!csr->cache_head) { - CSR1212_FREE(csr); - return NULL; - } - - /* The keyval key id is not used for the root node, but a valid key id - * that can be used for a directory needs to be passed to - * csr1212_new_directory(). */ - csr->root_kv = csr1212_new_directory(CSR1212_KV_ID_VENDOR); - if (!csr->root_kv) { - CSR1212_FREE(csr->cache_head); - CSR1212_FREE(csr); - return NULL; - } - - csr->bus_info_data = csr->cache_head->data; - csr->bus_info_len = bus_info_size; - csr->crc_len = bus_info_size; - csr->ops = ops; - csr->private = private; - csr->cache_tail = csr->cache_head; - - return csr; -} - -void csr1212_init_local_csr(struct csr1212_csr *csr, - const u32 *bus_info_data, int max_rom) -{ - static const int mr_map[] = { 4, 64, 1024, 0 }; - - BUG_ON(max_rom & ~0x3); - csr->max_rom = mr_map[max_rom]; - memcpy(csr->bus_info_data, bus_info_data, csr->bus_info_len); -} - -static struct csr1212_keyval *csr1212_new_keyval(u8 type, u8 key) -{ - struct csr1212_keyval *kv; - - if (key < 0x30 && ((csr1212_key_id_type_map[key] & (1 << type)) == 0)) - return NULL; - - kv = CSR1212_MALLOC(sizeof(*kv)); - if (!kv) - return NULL; - - atomic_set(&kv->refcnt, 1); - kv->key.type = type; - kv->key.id = key; - kv->associate = NULL; - kv->next = NULL; - kv->prev = NULL; - kv->offset = 0; - kv->valid = 0; - return kv; -} - -struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value) -{ - struct csr1212_keyval *kv; - - kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key); - if (!kv) - return NULL; - - kv->value.immediate = value; - kv->valid = 1; - return kv; -} - -static struct csr1212_keyval * -csr1212_new_leaf(u8 key, const void *data, size_t data_len) -{ - struct csr1212_keyval *kv; - - kv = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, key); - if (!kv) - return NULL; - - if (data_len > 0) { - kv->value.leaf.data = CSR1212_MALLOC(data_len); - if (!kv->value.leaf.data) { - CSR1212_FREE(kv); - return NULL; - } - - if (data) - memcpy(kv->value.leaf.data, data, data_len); - } else { - kv->value.leaf.data = NULL; - } - - kv->value.leaf.len = bytes_to_quads(data_len); - kv->offset = 0; - kv->valid = 1; - - return kv; -} - -static struct csr1212_keyval * -csr1212_new_csr_offset(u8 key, u32 csr_offset) -{ - struct csr1212_keyval *kv; - - kv = csr1212_new_keyval(CSR1212_KV_TYPE_CSR_OFFSET, key); - if (!kv) - return NULL; - - kv->value.csr_offset = csr_offset; - - kv->offset = 0; - kv->valid = 1; - return kv; -} - -struct csr1212_keyval *csr1212_new_directory(u8 key) -{ - struct csr1212_keyval *kv; - - kv = csr1212_new_keyval(CSR1212_KV_TYPE_DIRECTORY, key); - if (!kv) - return NULL; - - kv->value.directory.len = 0; - kv->offset = 0; - kv->value.directory.dentries_head = NULL; - kv->value.directory.dentries_tail = NULL; - kv->valid = 1; - return kv; -} - -void csr1212_associate_keyval(struct csr1212_keyval *kv, - struct csr1212_keyval *associate) -{ - BUG_ON(!kv || !associate || kv->key.id == CSR1212_KV_ID_DESCRIPTOR || - (associate->key.id != CSR1212_KV_ID_DESCRIPTOR && - associate->key.id != CSR1212_KV_ID_DEPENDENT_INFO && - associate->key.id != CSR1212_KV_ID_EXTENDED_KEY && - associate->key.id != CSR1212_KV_ID_EXTENDED_DATA && - associate->key.id < 0x30) || - (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID && - associate->key.id != CSR1212_KV_ID_EXTENDED_KEY) || - (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY && - associate->key.id != CSR1212_KV_ID_EXTENDED_DATA) || - (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY && - kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) || - (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA && - kv->key.id != CSR1212_KV_ID_EXTENDED_KEY)); - - if (kv->associate) - csr1212_release_keyval(kv->associate); - - csr1212_keep_keyval(associate); - kv->associate = associate; -} - -static int __csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, - struct csr1212_keyval *kv, - bool keep_keyval) -{ - struct csr1212_dentry *dentry; - - BUG_ON(!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY); - - dentry = CSR1212_MALLOC(sizeof(*dentry)); - if (!dentry) - return -ENOMEM; - - if (keep_keyval) - csr1212_keep_keyval(kv); - dentry->kv = kv; - - dentry->next = NULL; - dentry->prev = dir->value.directory.dentries_tail; - - if (!dir->value.directory.dentries_head) - dir->value.directory.dentries_head = dentry; - - if (dir->value.directory.dentries_tail) - dir->value.directory.dentries_tail->next = dentry; - dir->value.directory.dentries_tail = dentry; - - return CSR1212_SUCCESS; -} - -int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, - struct csr1212_keyval *kv) -{ - return __csr1212_attach_keyval_to_directory(dir, kv, true); -} - -#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \ - (&((kv)->value.leaf.data[1])) - -#define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \ - ((kv)->value.leaf.data[0] = \ - cpu_to_be32(CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) | \ - ((type) << CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT))) -#define CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, spec_id) \ - ((kv)->value.leaf.data[0] = \ - cpu_to_be32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \ - CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \ - ((spec_id) & CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK))) - -static struct csr1212_keyval * -csr1212_new_descriptor_leaf(u8 dtype, u32 specifier_id, - const void *data, size_t data_len) -{ - struct csr1212_keyval *kv; - - kv = csr1212_new_leaf(CSR1212_KV_ID_DESCRIPTOR, NULL, - data_len + CSR1212_DESCRIPTOR_LEAF_OVERHEAD); - if (!kv) - return NULL; - - kmemcheck_annotate_variable(kv->value.leaf.data[0]); - CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, dtype); - CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, specifier_id); - - if (data) - memcpy(CSR1212_DESCRIPTOR_LEAF_DATA(kv), data, data_len); - - return kv; -} - -/* Check if string conforms to minimal ASCII as per IEEE 1212 clause 7.4 */ -static int csr1212_check_minimal_ascii(const char *s) -{ - static const char minimal_ascii_table[] = { - /* 1 2 4 8 16 32 64 128 */ - 128, /* --, --, --, --, --, --, --, 07, */ - 4 + 16 + 32, /* --, --, 0a, --, 0C, 0D, --, --, */ - 0, /* --, --, --, --, --, --, --, --, */ - 0, /* --, --, --, --, --, --, --, --, */ - 255 - 8 - 16, /* 20, 21, 22, --, --, 25, 26, 27, */ - 255, /* 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, */ - 255, /* 30, 31, 32, 33, 34, 35, 36, 37, */ - 255, /* 38, 39, 3a, 3b, 3c, 3d, 3e, 3f, */ - 255, /* 40, 41, 42, 43, 44, 45, 46, 47, */ - 255, /* 48, 49, 4a, 4b, 4c, 4d, 4e, 4f, */ - 255, /* 50, 51, 52, 53, 54, 55, 56, 57, */ - 1 + 2 + 4 + 128, /* 58, 59, 5a, --, --, --, --, 5f, */ - 255 - 1, /* --, 61, 62, 63, 64, 65, 66, 67, */ - 255, /* 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, */ - 255, /* 70, 71, 72, 73, 74, 75, 76, 77, */ - 1 + 2 + 4, /* 78, 79, 7a, --, --, --, --, --, */ - }; - int i, j; - - for (; *s; s++) { - i = *s >> 3; /* i = *s / 8; */ - j = 1 << (*s & 3); /* j = 1 << (*s % 8); */ - - if (i >= ARRAY_SIZE(minimal_ascii_table) || - !(minimal_ascii_table[i] & j)) - return -EINVAL; - } - return 0; -} - -/* IEEE 1212 clause 7.5.4.1 textual descriptors (English, minimal ASCII) */ -struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s) -{ - struct csr1212_keyval *kv; - u32 *text; - size_t str_len, quads; - - if (!s || !*s || csr1212_check_minimal_ascii(s)) - return NULL; - - str_len = strlen(s); - quads = bytes_to_quads(str_len); - kv = csr1212_new_descriptor_leaf(0, 0, NULL, quads_to_bytes(quads) + - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD); - if (!kv) - return NULL; - - kv->value.leaf.data[1] = 0; /* width, character_set, language */ - text = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv); - text[quads - 1] = 0; /* padding */ - memcpy(text, s, str_len); - - return kv; -} - - -/* Destruction Routines */ - -void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir, - struct csr1212_keyval *kv) -{ - struct csr1212_dentry *dentry; - - if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY) - return; - - dentry = csr1212_find_keyval(dir, kv); - - if (!dentry) - return; - - if (dentry->prev) - dentry->prev->next = dentry->next; - if (dentry->next) - dentry->next->prev = dentry->prev; - if (dir->value.directory.dentries_head == dentry) - dir->value.directory.dentries_head = dentry->next; - if (dir->value.directory.dentries_tail == dentry) - dir->value.directory.dentries_tail = dentry->prev; - - CSR1212_FREE(dentry); - - csr1212_release_keyval(kv); -} - -/* This function is used to free the memory taken by a keyval. If the given - * keyval is a directory type, then any keyvals contained in that directory - * will be destroyed as well if noone holds a reference on them. By means of - * list manipulation, this routine will descend a directory structure in a - * non-recursive manner. */ -void csr1212_release_keyval(struct csr1212_keyval *kv) -{ - struct csr1212_keyval *k, *a; - struct csr1212_dentry dentry; - struct csr1212_dentry *head, *tail; - - if (!atomic_dec_and_test(&kv->refcnt)) - return; - - dentry.kv = kv; - dentry.next = NULL; - dentry.prev = NULL; - - head = &dentry; - tail = head; - - while (head) { - k = head->kv; - - while (k) { - /* must not dec_and_test kv->refcnt again */ - if (k != kv && !atomic_dec_and_test(&k->refcnt)) - break; - - a = k->associate; - - if (k->key.type == CSR1212_KV_TYPE_DIRECTORY) { - /* If the current entry is a directory, move all - * the entries to the destruction list. */ - if (k->value.directory.dentries_head) { - tail->next = - k->value.directory.dentries_head; - k->value.directory.dentries_head->prev = - tail; - tail = k->value.directory.dentries_tail; - } - } - free_keyval(k); - k = a; - } - - head = head->next; - if (head) { - if (head->prev && head->prev != &dentry) - CSR1212_FREE(head->prev); - head->prev = NULL; - } else if (tail != &dentry) { - CSR1212_FREE(tail); - } - } -} - -void csr1212_destroy_csr(struct csr1212_csr *csr) -{ - struct csr1212_csr_rom_cache *c, *oc; - struct csr1212_cache_region *cr, *ocr; - - csr1212_release_keyval(csr->root_kv); - - c = csr->cache_head; - while (c) { - oc = c; - cr = c->filled_head; - while (cr) { - ocr = cr; - cr = cr->next; - CSR1212_FREE(ocr); - } - c = c->next; - CSR1212_FREE(oc); - } - - CSR1212_FREE(csr); -} - - -/* CSR Image Creation */ - -static int csr1212_append_new_cache(struct csr1212_csr *csr, size_t romsize) -{ - struct csr1212_csr_rom_cache *cache; - u64 csr_addr; - - BUG_ON(!csr || !csr->ops || !csr->ops->allocate_addr_range || - !csr->ops->release_addr || csr->max_rom < 1); - - /* ROM size must be a multiple of csr->max_rom */ - romsize = (romsize + (csr->max_rom - 1)) & ~(csr->max_rom - 1); - - csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom, - csr->private); - if (csr_addr == CSR1212_INVALID_ADDR_SPACE) - return -ENOMEM; - - if (csr_addr < CSR1212_REGISTER_SPACE_BASE) { - /* Invalid address returned from allocate_addr_range(). */ - csr->ops->release_addr(csr_addr, csr->private); - return -ENOMEM; - } - - cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE, - romsize); - if (!cache) { - csr->ops->release_addr(csr_addr, csr->private); - return -ENOMEM; - } - - cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, - CSR1212_KV_ID_EXTENDED_ROM); - if (!cache->ext_rom) { - csr->ops->release_addr(csr_addr, csr->private); - CSR1212_FREE(cache); - return -ENOMEM; - } - - if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) != - CSR1212_SUCCESS) { - csr1212_release_keyval(cache->ext_rom); - csr->ops->release_addr(csr_addr, csr->private); - CSR1212_FREE(cache); - return -ENOMEM; - } - cache->ext_rom->offset = csr_addr - CSR1212_REGISTER_SPACE_BASE; - cache->ext_rom->value.leaf.len = -1; - cache->ext_rom->value.leaf.data = cache->data; - - /* Add cache to tail of cache list */ - cache->prev = csr->cache_tail; - csr->cache_tail->next = cache; - csr->cache_tail = cache; - return CSR1212_SUCCESS; -} - -static void csr1212_remove_cache(struct csr1212_csr *csr, - struct csr1212_csr_rom_cache *cache) -{ - if (csr->cache_head == cache) - csr->cache_head = cache->next; - if (csr->cache_tail == cache) - csr->cache_tail = cache->prev; - - if (cache->prev) - cache->prev->next = cache->next; - if (cache->next) - cache->next->prev = cache->prev; - - if (cache->ext_rom) { - csr1212_detach_keyval_from_directory(csr->root_kv, - cache->ext_rom); - csr1212_release_keyval(cache->ext_rom); - } - - CSR1212_FREE(cache); -} - -static int csr1212_generate_layout_subdir(struct csr1212_keyval *dir, - struct csr1212_keyval **layout_tail) -{ - struct csr1212_dentry *dentry; - struct csr1212_keyval *dkv; - struct csr1212_keyval *last_extkey_spec = NULL; - struct csr1212_keyval *last_extkey = NULL; - int num_entries = 0; - - for (dentry = dir->value.directory.dentries_head; dentry; - dentry = dentry->next) { - for (dkv = dentry->kv; dkv; dkv = dkv->associate) { - /* Special Case: Extended Key Specifier_ID */ - if (dkv->key.id == - CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { - if (last_extkey_spec == NULL) - last_extkey_spec = dkv; - else if (dkv->value.immediate != - last_extkey_spec->value.immediate) - last_extkey_spec = dkv; - else - continue; - /* Special Case: Extended Key */ - } else if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY) { - if (last_extkey == NULL) - last_extkey = dkv; - else if (dkv->value.immediate != - last_extkey->value.immediate) - last_extkey = dkv; - else - continue; - } - - num_entries += 1; - - switch (dkv->key.type) { - default: - case CSR1212_KV_TYPE_IMMEDIATE: - case CSR1212_KV_TYPE_CSR_OFFSET: - break; - case CSR1212_KV_TYPE_LEAF: - case CSR1212_KV_TYPE_DIRECTORY: - /* Remove from list */ - if (dkv->prev && (dkv->prev->next == dkv)) - dkv->prev->next = dkv->next; - if (dkv->next && (dkv->next->prev == dkv)) - dkv->next->prev = dkv->prev; - //if (dkv == *layout_tail) - // *layout_tail = dkv->prev; - - /* Special case: Extended ROM leafs */ - if (dkv->key.id == CSR1212_KV_ID_EXTENDED_ROM) { - dkv->value.leaf.len = -1; - /* Don't add Extended ROM leafs in the - * layout list, they are handled - * differently. */ - break; - } - - /* Add to tail of list */ - dkv->next = NULL; - dkv->prev = *layout_tail; - (*layout_tail)->next = dkv; - *layout_tail = dkv; - break; - } - } - } - return num_entries; -} - -static size_t csr1212_generate_layout_order(struct csr1212_keyval *kv) -{ - struct csr1212_keyval *ltail = kv; - size_t agg_size = 0; - - while (kv) { - switch (kv->key.type) { - case CSR1212_KV_TYPE_LEAF: - /* Add 1 quadlet for crc/len field */ - agg_size += kv->value.leaf.len + 1; - break; - - case CSR1212_KV_TYPE_DIRECTORY: - kv->value.directory.len = - csr1212_generate_layout_subdir(kv, <ail); - /* Add 1 quadlet for crc/len field */ - agg_size += kv->value.directory.len + 1; - break; - } - kv = kv->next; - } - return quads_to_bytes(agg_size); -} - -static struct csr1212_keyval * -csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, - struct csr1212_keyval *start_kv, int start_pos) -{ - struct csr1212_keyval *kv = start_kv; - struct csr1212_keyval *okv = start_kv; - int pos = start_pos; - int kv_len = 0, okv_len = 0; - - cache->layout_head = kv; - - while (kv && pos < cache->size) { - /* Special case: Extended ROM leafs */ - if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) - kv->offset = cache->offset + pos; - - switch (kv->key.type) { - case CSR1212_KV_TYPE_LEAF: - kv_len = kv->value.leaf.len; - break; - - case CSR1212_KV_TYPE_DIRECTORY: - kv_len = kv->value.directory.len; - break; - - default: - /* Should never get here */ - WARN_ON(1); - break; - } - - pos += quads_to_bytes(kv_len + 1); - - if (pos <= cache->size) { - okv = kv; - okv_len = kv_len; - kv = kv->next; - } - } - - cache->layout_tail = okv; - cache->len = okv->offset - cache->offset + quads_to_bytes(okv_len + 1); - - return kv; -} - -#define CSR1212_KV_KEY_SHIFT 24 -#define CSR1212_KV_KEY_TYPE_SHIFT 6 -#define CSR1212_KV_KEY_ID_MASK 0x3f -#define CSR1212_KV_KEY_TYPE_MASK 0x3 /* after shift */ - -static void -csr1212_generate_tree_subdir(struct csr1212_keyval *dir, u32 *data_buffer) -{ - struct csr1212_dentry *dentry; - struct csr1212_keyval *last_extkey_spec = NULL; - struct csr1212_keyval *last_extkey = NULL; - int index = 0; - - for (dentry = dir->value.directory.dentries_head; - dentry; - dentry = dentry->next) { - struct csr1212_keyval *a; - - for (a = dentry->kv; a; a = a->associate) { - u32 value = 0; - - /* Special Case: Extended Key Specifier_ID */ - if (a->key.id == - CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { - if (last_extkey_spec == NULL) - last_extkey_spec = a; - else if (a->value.immediate != - last_extkey_spec->value.immediate) - last_extkey_spec = a; - else - continue; - - /* Special Case: Extended Key */ - } else if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY) { - if (last_extkey == NULL) - last_extkey = a; - else if (a->value.immediate != - last_extkey->value.immediate) - last_extkey = a; - else - continue; - } - - switch (a->key.type) { - case CSR1212_KV_TYPE_IMMEDIATE: - value = a->value.immediate; - break; - case CSR1212_KV_TYPE_CSR_OFFSET: - value = a->value.csr_offset; - break; - case CSR1212_KV_TYPE_LEAF: - value = a->offset; - value -= dir->offset + quads_to_bytes(1+index); - value = bytes_to_quads(value); - break; - case CSR1212_KV_TYPE_DIRECTORY: - value = a->offset; - value -= dir->offset + quads_to_bytes(1+index); - value = bytes_to_quads(value); - break; - default: - /* Should never get here */ - WARN_ON(1); - break; - } - - value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) << - CSR1212_KV_KEY_SHIFT; - value |= (a->key.type & CSR1212_KV_KEY_TYPE_MASK) << - (CSR1212_KV_KEY_SHIFT + - CSR1212_KV_KEY_TYPE_SHIFT); - data_buffer[index] = cpu_to_be32(value); - index++; - } - } -} - -struct csr1212_keyval_img { - u16 length; - u16 crc; - - /* Must be last */ - u32 data[0]; /* older gcc can't handle [] which is standard */ -}; - -static void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache) -{ - struct csr1212_keyval *kv, *nkv; - struct csr1212_keyval_img *kvi; - - for (kv = cache->layout_head; - kv != cache->layout_tail->next; - kv = nkv) { - kvi = (struct csr1212_keyval_img *)(cache->data + - bytes_to_quads(kv->offset - cache->offset)); - switch (kv->key.type) { - default: - case CSR1212_KV_TYPE_IMMEDIATE: - case CSR1212_KV_TYPE_CSR_OFFSET: - /* Should never get here */ - WARN_ON(1); - break; - - case CSR1212_KV_TYPE_LEAF: - /* Don't copy over Extended ROM areas, they are - * already filled out! */ - if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) - memcpy(kvi->data, kv->value.leaf.data, - quads_to_bytes(kv->value.leaf.len)); - - kvi->length = cpu_to_be16(kv->value.leaf.len); - kvi->crc = csr1212_crc16(kvi->data, kv->value.leaf.len); - break; - - case CSR1212_KV_TYPE_DIRECTORY: - csr1212_generate_tree_subdir(kv, kvi->data); - - kvi->length = cpu_to_be16(kv->value.directory.len); - kvi->crc = csr1212_crc16(kvi->data, - kv->value.directory.len); - break; - } - - nkv = kv->next; - if (kv->prev) - kv->prev->next = NULL; - if (kv->next) - kv->next->prev = NULL; - kv->prev = NULL; - kv->next = NULL; - } -} - -/* This size is arbitrarily chosen. - * The struct overhead is subtracted for more economic allocations. */ -#define CSR1212_EXTENDED_ROM_SIZE (2048 - sizeof(struct csr1212_csr_rom_cache)) - -int csr1212_generate_csr_image(struct csr1212_csr *csr) -{ - struct csr1212_bus_info_block_img *bi; - struct csr1212_csr_rom_cache *cache; - struct csr1212_keyval *kv; - size_t agg_size; - int ret; - int init_offset; - - BUG_ON(!csr); - - cache = csr->cache_head; - - bi = (struct csr1212_bus_info_block_img*)cache->data; - - bi->length = bytes_to_quads(csr->bus_info_len) - 1; - bi->crc_length = bi->length; - bi->crc = csr1212_crc16(bi->data, bi->crc_length); - - csr->root_kv->next = NULL; - csr->root_kv->prev = NULL; - - agg_size = csr1212_generate_layout_order(csr->root_kv); - - init_offset = csr->bus_info_len; - - for (kv = csr->root_kv, cache = csr->cache_head; - kv; - cache = cache->next) { - if (!cache) { - /* Estimate approximate number of additional cache - * regions needed (it assumes that the cache holding - * the first 1K Config ROM space always exists). */ - int est_c = agg_size / (CSR1212_EXTENDED_ROM_SIZE - - (2 * sizeof(u32))) + 1; - - /* Add additional cache regions, extras will be - * removed later */ - for (; est_c; est_c--) { - ret = csr1212_append_new_cache(csr, - CSR1212_EXTENDED_ROM_SIZE); - if (ret != CSR1212_SUCCESS) - return ret; - } - /* Need to re-layout for additional cache regions */ - agg_size = csr1212_generate_layout_order(csr->root_kv); - kv = csr->root_kv; - cache = csr->cache_head; - init_offset = csr->bus_info_len; - } - kv = csr1212_generate_positions(cache, kv, init_offset); - agg_size -= cache->len; - init_offset = sizeof(u32); - } - - /* Remove unused, excess cache regions */ - while (cache) { - struct csr1212_csr_rom_cache *oc = cache; - - cache = cache->next; - csr1212_remove_cache(csr, oc); - } - - /* Go through the list backward so that when done, the correct CRC - * will be calculated for the Extended ROM areas. */ - for (cache = csr->cache_tail; cache; cache = cache->prev) { - /* Only Extended ROM caches should have this set. */ - if (cache->ext_rom) { - int leaf_size; - - /* Make sure the Extended ROM leaf is a multiple of - * max_rom in size. */ - BUG_ON(csr->max_rom < 1); - leaf_size = (cache->len + (csr->max_rom - 1)) & - ~(csr->max_rom - 1); - - /* Zero out the unused ROM region */ - memset(cache->data + bytes_to_quads(cache->len), 0x00, - leaf_size - cache->len); - - /* Subtract leaf header */ - leaf_size -= sizeof(u32); - - /* Update the Extended ROM leaf length */ - cache->ext_rom->value.leaf.len = - bytes_to_quads(leaf_size); - } else { - /* Zero out the unused ROM region */ - memset(cache->data + bytes_to_quads(cache->len), 0x00, - cache->size - cache->len); - } - - /* Copy the data into the cache buffer */ - csr1212_fill_cache(cache); - - if (cache != csr->cache_head) { - /* Set the length and CRC of the extended ROM. */ - struct csr1212_keyval_img *kvi = - (struct csr1212_keyval_img*)cache->data; - u16 len = bytes_to_quads(cache->len) - 1; - - kvi->length = cpu_to_be16(len); - kvi->crc = csr1212_crc16(kvi->data, len); - } - } - - return CSR1212_SUCCESS; -} - -int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, u32 len) -{ - struct csr1212_csr_rom_cache *cache; - - for (cache = csr->cache_head; cache; cache = cache->next) - if (offset >= cache->offset && - (offset + len) <= (cache->offset + cache->size)) { - memcpy(buffer, &cache->data[ - bytes_to_quads(offset - cache->offset)], - len); - return CSR1212_SUCCESS; - } - - return -ENOENT; -} - -/* - * Apparently there are many different wrong implementations of the CRC - * algorithm. We don't fail, we just warn... approximately once per GUID. - */ -static void -csr1212_check_crc(const u32 *buffer, size_t length, u16 crc, __be32 *guid) -{ - static u64 last_bad_eui64; - u64 eui64 = ((u64)be32_to_cpu(guid[0]) << 32) | be32_to_cpu(guid[1]); - - if (csr1212_crc16(buffer, length) == crc || - csr1212_msft_crc16(buffer, length) == crc || - eui64 == last_bad_eui64) - return; - - printk(KERN_DEBUG "ieee1394: config ROM CRC error\n"); - last_bad_eui64 = eui64; -} - -/* Parse a chunk of data as a Config ROM */ - -static int csr1212_parse_bus_info_block(struct csr1212_csr *csr) -{ - struct csr1212_bus_info_block_img *bi; - struct csr1212_cache_region *cr; - int i; - int ret; - - for (i = 0; i < csr->bus_info_len; i += sizeof(u32)) { - ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, - &csr->cache_head->data[bytes_to_quads(i)], - csr->private); - if (ret != CSR1212_SUCCESS) - return ret; - - /* check ROM header's info_length */ - if (i == 0 && - be32_to_cpu(csr->cache_head->data[0]) >> 24 != - bytes_to_quads(csr->bus_info_len) - 1) - return -EINVAL; - } - - bi = (struct csr1212_bus_info_block_img*)csr->cache_head->data; - csr->crc_len = quads_to_bytes(bi->crc_length); - - /* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that - * is not always the case, so read the rest of the crc area 1 quadlet at - * a time. */ - for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(u32)) { - ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, - &csr->cache_head->data[bytes_to_quads(i)], - csr->private); - if (ret != CSR1212_SUCCESS) - return ret; - } - - csr1212_check_crc(bi->data, bi->crc_length, bi->crc, - &csr->bus_info_data[3]); - - cr = CSR1212_MALLOC(sizeof(*cr)); - if (!cr) - return -ENOMEM; - - cr->next = NULL; - cr->prev = NULL; - cr->offset_start = 0; - cr->offset_end = csr->crc_len + 4; - - csr->cache_head->filled_head = cr; - csr->cache_head->filled_tail = cr; - - return CSR1212_SUCCESS; -} - -#define CSR1212_KV_KEY(q) (be32_to_cpu(q) >> CSR1212_KV_KEY_SHIFT) -#define CSR1212_KV_KEY_TYPE(q) (CSR1212_KV_KEY(q) >> CSR1212_KV_KEY_TYPE_SHIFT) -#define CSR1212_KV_KEY_ID(q) (CSR1212_KV_KEY(q) & CSR1212_KV_KEY_ID_MASK) -#define CSR1212_KV_VAL_MASK 0xffffff -#define CSR1212_KV_VAL(q) (be32_to_cpu(q) & CSR1212_KV_VAL_MASK) - -static int -csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) -{ - int ret = CSR1212_SUCCESS; - struct csr1212_keyval *k = NULL; - u32 offset; - bool keep_keyval = true; - - switch (CSR1212_KV_KEY_TYPE(ki)) { - case CSR1212_KV_TYPE_IMMEDIATE: - k = csr1212_new_immediate(CSR1212_KV_KEY_ID(ki), - CSR1212_KV_VAL(ki)); - if (!k) { - ret = -ENOMEM; - goto out; - } - /* Don't keep local reference when parsing. */ - keep_keyval = false; - break; - - case CSR1212_KV_TYPE_CSR_OFFSET: - k = csr1212_new_csr_offset(CSR1212_KV_KEY_ID(ki), - CSR1212_KV_VAL(ki)); - if (!k) { - ret = -ENOMEM; - goto out; - } - /* Don't keep local reference when parsing. */ - keep_keyval = false; - break; - - default: - /* Compute the offset from 0xffff f000 0000. */ - offset = quads_to_bytes(CSR1212_KV_VAL(ki)) + kv_pos; - if (offset == kv_pos) { - /* Uh-oh. Can't have a relative offset of 0 for Leaves - * or Directories. The Config ROM image is most likely - * messed up, so we'll just abort here. */ - ret = -EIO; - goto out; - } - - k = csr1212_find_keyval_offset(dir, offset); - - if (k) - break; /* Found it. */ - - if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY) - k = csr1212_new_directory(CSR1212_KV_KEY_ID(ki)); - else - k = csr1212_new_leaf(CSR1212_KV_KEY_ID(ki), NULL, 0); - - if (!k) { - ret = -ENOMEM; - goto out; - } - /* Don't keep local reference when parsing. */ - keep_keyval = false; - /* Contents not read yet so it's not valid. */ - k->valid = 0; - k->offset = offset; - - k->prev = dir; - k->next = dir->next; - dir->next->prev = k; - dir->next = k; - } - ret = __csr1212_attach_keyval_to_directory(dir, k, keep_keyval); -out: - if (ret != CSR1212_SUCCESS && k != NULL) - free_keyval(k); - return ret; -} - -int csr1212_parse_keyval(struct csr1212_keyval *kv, - struct csr1212_csr_rom_cache *cache) -{ - struct csr1212_keyval_img *kvi; - int i; - int ret = CSR1212_SUCCESS; - int kvi_len; - - kvi = (struct csr1212_keyval_img*) - &cache->data[bytes_to_quads(kv->offset - cache->offset)]; - kvi_len = be16_to_cpu(kvi->length); - - /* GUID is wrong in here in case of extended ROM. We don't care. */ - csr1212_check_crc(kvi->data, kvi_len, kvi->crc, &cache->data[3]); - - switch (kv->key.type) { - case CSR1212_KV_TYPE_DIRECTORY: - for (i = 0; i < kvi_len; i++) { - u32 ki = kvi->data[i]; - - /* Some devices put null entries in their unit - * directories. If we come across such an entry, - * then skip it. */ - if (ki == 0x0) - continue; - ret = csr1212_parse_dir_entry(kv, ki, - kv->offset + quads_to_bytes(i + 1)); - } - kv->value.directory.len = kvi_len; - break; - - case CSR1212_KV_TYPE_LEAF: - if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) { - size_t size = quads_to_bytes(kvi_len); - - kv->value.leaf.data = CSR1212_MALLOC(size); - if (!kv->value.leaf.data) { - ret = -ENOMEM; - goto out; - } - - kv->value.leaf.len = kvi_len; - memcpy(kv->value.leaf.data, kvi->data, size); - } - break; - } - - kv->valid = 1; -out: - return ret; -} - -static int -csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) -{ - struct csr1212_cache_region *cr, *ncr, *newcr = NULL; - struct csr1212_keyval_img *kvi = NULL; - struct csr1212_csr_rom_cache *cache; - int cache_index; - u64 addr; - u32 *cache_ptr; - u16 kv_len = 0; - - BUG_ON(!csr || !kv || csr->max_rom < 1); - - /* First find which cache the data should be in (or go in if not read - * yet). */ - for (cache = csr->cache_head; cache; cache = cache->next) - if (kv->offset >= cache->offset && - kv->offset < (cache->offset + cache->size)) - break; - - if (!cache) { - u32 q, cache_size; - - /* Only create a new cache for Extended ROM leaves. */ - if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) - return -EINVAL; - - if (csr->ops->bus_read(csr, - CSR1212_REGISTER_SPACE_BASE + kv->offset, - &q, csr->private)) - return -EIO; - - kv->value.leaf.len = be32_to_cpu(q) >> 16; - - cache_size = (quads_to_bytes(kv->value.leaf.len + 1) + - (csr->max_rom - 1)) & ~(csr->max_rom - 1); - - cache = csr1212_rom_cache_malloc(kv->offset, cache_size); - if (!cache) - return -ENOMEM; - - kv->value.leaf.data = &cache->data[1]; - csr->cache_tail->next = cache; - cache->prev = csr->cache_tail; - cache->next = NULL; - csr->cache_tail = cache; - cache->filled_head = - CSR1212_MALLOC(sizeof(*cache->filled_head)); - if (!cache->filled_head) - return -ENOMEM; - - cache->filled_head->offset_start = 0; - cache->filled_head->offset_end = sizeof(u32); - cache->filled_tail = cache->filled_head; - cache->filled_head->next = NULL; - cache->filled_head->prev = NULL; - cache->data[0] = q; - - /* Don't read the entire extended ROM now. Pieces of it will - * be read when entries inside it are read. */ - return csr1212_parse_keyval(kv, cache); - } - - cache_index = kv->offset - cache->offset; - - /* Now seach read portions of the cache to see if it is there. */ - for (cr = cache->filled_head; cr; cr = cr->next) { - if (cache_index < cr->offset_start) { - newcr = CSR1212_MALLOC(sizeof(*newcr)); - if (!newcr) - return -ENOMEM; - - newcr->offset_start = cache_index & ~(csr->max_rom - 1); - newcr->offset_end = newcr->offset_start; - newcr->next = cr; - newcr->prev = cr->prev; - cr->prev = newcr; - cr = newcr; - break; - } else if ((cache_index >= cr->offset_start) && - (cache_index < cr->offset_end)) { - kvi = (struct csr1212_keyval_img*) - (&cache->data[bytes_to_quads(cache_index)]); - kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1); - break; - } else if (cache_index == cr->offset_end) { - break; - } - } - - if (!cr) { - cr = cache->filled_tail; - newcr = CSR1212_MALLOC(sizeof(*newcr)); - if (!newcr) - return -ENOMEM; - - newcr->offset_start = cache_index & ~(csr->max_rom - 1); - newcr->offset_end = newcr->offset_start; - newcr->prev = cr; - newcr->next = cr->next; - cr->next = newcr; - cr = newcr; - cache->filled_tail = newcr; - } - - while(!kvi || cr->offset_end < cache_index + kv_len) { - cache_ptr = &cache->data[bytes_to_quads(cr->offset_end & - ~(csr->max_rom - 1))]; - - addr = (CSR1212_CSR_ARCH_REG_SPACE_BASE + cache->offset + - cr->offset_end) & ~(csr->max_rom - 1); - - if (csr->ops->bus_read(csr, addr, cache_ptr, csr->private)) - return -EIO; - - cr->offset_end += csr->max_rom - (cr->offset_end & - (csr->max_rom - 1)); - - if (!kvi && (cr->offset_end > cache_index)) { - kvi = (struct csr1212_keyval_img*) - (&cache->data[bytes_to_quads(cache_index)]); - kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1); - } - - if ((kv_len + (kv->offset - cache->offset)) > cache->size) { - /* The Leaf or Directory claims its length extends - * beyond the ConfigROM image region and thus beyond the - * end of our cache region. Therefore, we abort now - * rather than seg faulting later. */ - return -EIO; - } - - ncr = cr->next; - - if (ncr && (cr->offset_end >= ncr->offset_start)) { - /* consolidate region entries */ - ncr->offset_start = cr->offset_start; - - if (cr->prev) - cr->prev->next = cr->next; - ncr->prev = cr->prev; - if (cache->filled_head == cr) - cache->filled_head = ncr; - CSR1212_FREE(cr); - cr = ncr; - } - } - - return csr1212_parse_keyval(kv, cache); -} - -struct csr1212_keyval * -csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) -{ - if (!kv) - return NULL; - if (!kv->valid) - if (csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS) - return NULL; - return kv; -} - -int csr1212_parse_csr(struct csr1212_csr *csr) -{ - struct csr1212_dentry *dentry; - int ret; - - BUG_ON(!csr || !csr->ops || !csr->ops->bus_read); - - ret = csr1212_parse_bus_info_block(csr); - if (ret != CSR1212_SUCCESS) - return ret; - - /* - * There has been a buggy firmware with bus_info_block.max_rom > 0 - * spotted which actually only supported quadlet read requests to the - * config ROM. Therefore read everything quadlet by quadlet regardless - * of what the bus info block says. - */ - csr->max_rom = 4; - - csr->cache_head->layout_head = csr->root_kv; - csr->cache_head->layout_tail = csr->root_kv; - - csr->root_kv->offset = (CSR1212_CONFIG_ROM_SPACE_BASE & 0xffff) + - csr->bus_info_len; - - csr->root_kv->valid = 0; - csr->root_kv->next = csr->root_kv; - csr->root_kv->prev = csr->root_kv; - ret = csr1212_read_keyval(csr, csr->root_kv); - if (ret != CSR1212_SUCCESS) - return ret; - - /* Scan through the Root directory finding all extended ROM regions - * and make cache regions for them */ - for (dentry = csr->root_kv->value.directory.dentries_head; - dentry; dentry = dentry->next) { - if (dentry->kv->key.id == CSR1212_KV_ID_EXTENDED_ROM && - !dentry->kv->valid) { - ret = csr1212_read_keyval(csr, dentry->kv); - if (ret != CSR1212_SUCCESS) - return ret; - } - } - - return CSR1212_SUCCESS; -} diff --git a/drivers/ieee1394/csr1212.h b/drivers/ieee1394/csr1212.h deleted file mode 100644 index a892d922dbc9..000000000000 --- a/drivers/ieee1394/csr1212.h +++ /dev/null @@ -1,383 +0,0 @@ -/* - * csr1212.h -- IEEE 1212 Control and Status Register support for Linux - * - * Copyright (C) 2003 Francois Retief - * Steve Kinneberg - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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. - */ - -#ifndef __CSR1212_H__ -#define __CSR1212_H__ - -#include -#include -#include - -#define CSR1212_MALLOC(size) kmalloc((size), GFP_KERNEL) -#define CSR1212_FREE(ptr) kfree(ptr) - -#define CSR1212_SUCCESS (0) - - -/* CSR 1212 key types */ -#define CSR1212_KV_TYPE_IMMEDIATE 0 -#define CSR1212_KV_TYPE_CSR_OFFSET 1 -#define CSR1212_KV_TYPE_LEAF 2 -#define CSR1212_KV_TYPE_DIRECTORY 3 - - -/* CSR 1212 key ids */ -#define CSR1212_KV_ID_DESCRIPTOR 0x01 -#define CSR1212_KV_ID_BUS_DEPENDENT_INFO 0x02 -#define CSR1212_KV_ID_VENDOR 0x03 -#define CSR1212_KV_ID_HARDWARE_VERSION 0x04 -#define CSR1212_KV_ID_MODULE 0x07 -#define CSR1212_KV_ID_NODE_CAPABILITIES 0x0C -#define CSR1212_KV_ID_EUI_64 0x0D -#define CSR1212_KV_ID_UNIT 0x11 -#define CSR1212_KV_ID_SPECIFIER_ID 0x12 -#define CSR1212_KV_ID_VERSION 0x13 -#define CSR1212_KV_ID_DEPENDENT_INFO 0x14 -#define CSR1212_KV_ID_UNIT_LOCATION 0x15 -#define CSR1212_KV_ID_MODEL 0x17 -#define CSR1212_KV_ID_INSTANCE 0x18 -#define CSR1212_KV_ID_KEYWORD 0x19 -#define CSR1212_KV_ID_FEATURE 0x1A -#define CSR1212_KV_ID_EXTENDED_ROM 0x1B -#define CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID 0x1C -#define CSR1212_KV_ID_EXTENDED_KEY 0x1D -#define CSR1212_KV_ID_EXTENDED_DATA 0x1E -#define CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR 0x1F -#define CSR1212_KV_ID_DIRECTORY_ID 0x20 -#define CSR1212_KV_ID_REVISION 0x21 - - -/* IEEE 1212 Address space map */ -#define CSR1212_ALL_SPACE_BASE (0x000000000000ULL) -#define CSR1212_ALL_SPACE_SIZE (1ULL << 48) -#define CSR1212_ALL_SPACE_END (CSR1212_ALL_SPACE_BASE + CSR1212_ALL_SPACE_SIZE) - -#define CSR1212_MEMORY_SPACE_BASE (0x000000000000ULL) -#define CSR1212_MEMORY_SPACE_SIZE ((256ULL * (1ULL << 40)) - (512ULL * (1ULL << 20))) -#define CSR1212_MEMORY_SPACE_END (CSR1212_MEMORY_SPACE_BASE + CSR1212_MEMORY_SPACE_SIZE) - -#define CSR1212_PRIVATE_SPACE_BASE (0xffffe0000000ULL) -#define CSR1212_PRIVATE_SPACE_SIZE (256ULL * (1ULL << 20)) -#define CSR1212_PRIVATE_SPACE_END (CSR1212_PRIVATE_SPACE_BASE + CSR1212_PRIVATE_SPACE_SIZE) - -#define CSR1212_REGISTER_SPACE_BASE (0xfffff0000000ULL) -#define CSR1212_REGISTER_SPACE_SIZE (256ULL * (1ULL << 20)) -#define CSR1212_REGISTER_SPACE_END (CSR1212_REGISTER_SPACE_BASE + CSR1212_REGISTER_SPACE_SIZE) - -#define CSR1212_CSR_ARCH_REG_SPACE_BASE (0xfffff0000000ULL) -#define CSR1212_CSR_ARCH_REG_SPACE_SIZE (512) -#define CSR1212_CSR_ARCH_REG_SPACE_END (CSR1212_CSR_ARCH_REG_SPACE_BASE + CSR1212_CSR_ARCH_REG_SPACE_SIZE) -#define CSR1212_CSR_ARCH_REG_SPACE_OFFSET (CSR1212_CSR_ARCH_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) - -#define CSR1212_CSR_BUS_DEP_REG_SPACE_BASE (0xfffff0000200ULL) -#define CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE (512) -#define CSR1212_CSR_BUS_DEP_REG_SPACE_END (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE + CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE) -#define CSR1212_CSR_BUS_DEP_REG_SPACE_OFFSET (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) - -#define CSR1212_CONFIG_ROM_SPACE_BASE (0xfffff0000400ULL) -#define CSR1212_CONFIG_ROM_SPACE_SIZE (1024) -#define CSR1212_CONFIG_ROM_SPACE_END (CSR1212_CONFIG_ROM_SPACE_BASE + CSR1212_CONFIG_ROM_SPACE_SIZE) -#define CSR1212_CONFIG_ROM_SPACE_OFFSET (CSR1212_CONFIG_ROM_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) - -#define CSR1212_UNITS_SPACE_BASE (0xfffff0000800ULL) -#define CSR1212_UNITS_SPACE_SIZE ((256ULL * (1ULL << 20)) - 2048) -#define CSR1212_UNITS_SPACE_END (CSR1212_UNITS_SPACE_BASE + CSR1212_UNITS_SPACE_SIZE) -#define CSR1212_UNITS_SPACE_OFFSET (CSR1212_UNITS_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) - -#define CSR1212_INVALID_ADDR_SPACE -1 - - -/* Config ROM image structures */ -struct csr1212_bus_info_block_img { - u8 length; - u8 crc_length; - u16 crc; - - /* Must be last */ - u32 data[0]; /* older gcc can't handle [] which is standard */ -}; - -struct csr1212_leaf { - int len; - u32 *data; -}; - -struct csr1212_dentry { - struct csr1212_dentry *next, *prev; - struct csr1212_keyval *kv; -}; - -struct csr1212_directory { - int len; - struct csr1212_dentry *dentries_head, *dentries_tail; -}; - -struct csr1212_keyval { - struct { - u8 type; - u8 id; - } key; - union { - u32 immediate; - u32 csr_offset; - struct csr1212_leaf leaf; - struct csr1212_directory directory; - } value; - struct csr1212_keyval *associate; - atomic_t refcnt; - - /* used in generating and/or parsing CSR image */ - struct csr1212_keyval *next, *prev; /* flat list of CSR elements */ - u32 offset; /* position in CSR from 0xffff f000 0000 */ - u8 valid; /* flag indicating keyval has valid data*/ -}; - - -struct csr1212_cache_region { - struct csr1212_cache_region *next, *prev; - u32 offset_start; /* inclusive */ - u32 offset_end; /* exclusive */ -}; - -struct csr1212_csr_rom_cache { - struct csr1212_csr_rom_cache *next, *prev; - struct csr1212_cache_region *filled_head, *filled_tail; - struct csr1212_keyval *layout_head, *layout_tail; - size_t size; - u32 offset; - struct csr1212_keyval *ext_rom; - size_t len; - - /* Must be last */ - u32 data[0]; /* older gcc can't handle [] which is standard */ -}; - -struct csr1212_csr { - size_t bus_info_len; /* bus info block length in bytes */ - size_t crc_len; /* crc length in bytes */ - __be32 *bus_info_data; /* bus info data incl bus name and EUI */ - - void *private; /* private, bus specific data */ - struct csr1212_bus_ops *ops; - - struct csr1212_keyval *root_kv; - - int max_rom; /* max bytes readable in Config ROM region */ - - /* Items below used for image parsing and generation */ - struct csr1212_csr_rom_cache *cache_head, *cache_tail; -}; - -struct csr1212_bus_ops { - /* This function is used by csr1212 to read additional information - * from remote nodes when parsing a Config ROM (i.e., read Config ROM - * entries located in the Units Space. Must return 0 on success - * anything else indicates an error. */ - int (*bus_read) (struct csr1212_csr *csr, u64 addr, - void *buffer, void *private); - - /* This function is used by csr1212 to allocate a region in units space - * in the event that Config ROM entries don't all fit in the predefined - * 1K region. The void *private parameter is private member of struct - * csr1212_csr. */ - u64 (*allocate_addr_range) (u64 size, u32 alignment, void *private); - - /* This function is used by csr1212 to release a region in units space - * that is no longer needed. */ - void (*release_addr) (u64 addr, void *private); -}; - - -/* Descriptor Leaf manipulation macros */ -#define CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT 24 -#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK 0xffffff -#define CSR1212_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32)) - -#define CSR1212_DESCRIPTOR_LEAF_TYPE(kv) \ - (be32_to_cpu((kv)->value.leaf.data[0]) >> \ - CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) -#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) \ - (be32_to_cpu((kv)->value.leaf.data[0]) & \ - CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK) - - -/* Text Descriptor Leaf manipulation macros */ -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT 28 -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK 0xf /* after shift */ -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT 16 -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK 0xfff /* after shift */ -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32)) - -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) \ - (be32_to_cpu((kv)->value.leaf.data[1]) >> \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT) -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) \ - ((be32_to_cpu((kv)->value.leaf.data[1]) >> \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT) & \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) \ - (be32_to_cpu((kv)->value.leaf.data[1]) & \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK) -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv) \ - (&((kv)->value.leaf.data[2])) - - -/* The following 2 function are for creating new Configuration ROM trees. The - * first function is used for both creating local trees and parsing remote - * trees. The second function adds pertinent information to local Configuration - * ROM trees - namely data for the bus information block. */ -extern struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops, - size_t bus_info_size, - void *private); -extern void csr1212_init_local_csr(struct csr1212_csr *csr, - const u32 *bus_info_data, int max_rom); - - -/* Destroy a Configuration ROM tree and release all memory taken by the tree. */ -extern void csr1212_destroy_csr(struct csr1212_csr *csr); - - -/* The following set of functions are fore creating new keyvals for placement in - * a Configuration ROM tree. Code that creates new keyvals with these functions - * must release those keyvals with csr1212_release_keyval() when they are no - * longer needed. */ -extern struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value); -extern struct csr1212_keyval *csr1212_new_directory(u8 key); -extern struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s); - - -/* The following function manages association between keyvals. Typically, - * Descriptor Leaves and Directories will be associated with another keyval and - * it is desirable for the Descriptor keyval to be place immediately after the - * keyval that it is associated with. - * Take care with subsequent ROM modifications: There is no function to remove - * previously specified associations. - */ -extern void csr1212_associate_keyval(struct csr1212_keyval *kv, - struct csr1212_keyval *associate); - - -/* The following functions manage the association of a keyval and directories. - * A keyval may be attached to more than one directory. */ -extern int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, - struct csr1212_keyval *kv); -extern void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir, - struct csr1212_keyval *kv); - - -/* Creates a complete Configuration ROM image in the list of caches available - * via csr->cache_head. */ -extern int csr1212_generate_csr_image(struct csr1212_csr *csr); - - -/* This is a convience function for reading a block of data out of one of the - * caches in the csr->cache_head list. */ -extern int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, - u32 len); - - -/* The following functions are in place for parsing Configuration ROM images. - * csr1212_parse_keyval() is used should there be a need to directly parse a - * Configuration ROM directly. */ -extern int csr1212_parse_keyval(struct csr1212_keyval *kv, - struct csr1212_csr_rom_cache *cache); -extern int csr1212_parse_csr(struct csr1212_csr *csr); - - -/* This function allocates a new cache which may be used for either parsing or - * generating sub-sets of Configuration ROM images. */ -static inline struct csr1212_csr_rom_cache * -csr1212_rom_cache_malloc(u32 offset, size_t size) -{ - struct csr1212_csr_rom_cache *cache; - - cache = CSR1212_MALLOC(sizeof(*cache) + size); - if (!cache) - return NULL; - - cache->next = NULL; - cache->prev = NULL; - cache->filled_head = NULL; - cache->filled_tail = NULL; - cache->layout_head = NULL; - cache->layout_tail = NULL; - cache->offset = offset; - cache->size = size; - cache->ext_rom = NULL; - - return cache; -} - - -/* This function ensures that a keyval contains data when referencing a keyval - * created by parsing a Configuration ROM. */ -extern struct csr1212_keyval * -csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv); - - -/* This function increments the reference count for a keyval should there be a - * need for code to retain a keyval that has been parsed. */ -static inline void csr1212_keep_keyval(struct csr1212_keyval *kv) -{ - atomic_inc(&kv->refcnt); - smp_mb__after_atomic_inc(); -} - - -/* This function decrements a keyval's reference count and will destroy the - * keyval when there are no more users of the keyval. This should be called by - * any code that calls csr1212_keep_keyval() or any of the keyval creation - * routines csr1212_new_*(). */ -extern void csr1212_release_keyval(struct csr1212_keyval *kv); - - -/* - * This macro allows for looping over the keyval entries in a directory and it - * ensures that keyvals from remote ConfigROMs are parsed properly. - * - * struct csr1212_csr *_csr points to the CSR associated with dir. - * struct csr1212_keyval *_kv points to the current keyval (loop index). - * struct csr1212_keyval *_dir points to the directory to be looped. - * struct csr1212_dentry *_pos is used internally for indexing. - * - * kv will be NULL upon exit of the loop. - */ -#define csr1212_for_each_dir_entry(_csr, _kv, _dir, _pos) \ - for (csr1212_get_keyval((_csr), (_dir)), \ - _pos = (_dir)->value.directory.dentries_head, \ - _kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : NULL;\ - (_kv) && (_pos); \ - (_kv->associate == NULL) ? \ - ((_pos = _pos->next), (_kv = (_pos) ? \ - csr1212_get_keyval((_csr), _pos->kv) : \ - NULL)) : \ - (_kv = csr1212_get_keyval((_csr), _kv->associate))) - -#endif /* __CSR1212_H__ */ diff --git a/drivers/ieee1394/dma.c b/drivers/ieee1394/dma.c deleted file mode 100644 index d178699b194a..000000000000 --- a/drivers/ieee1394/dma.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * DMA region bookkeeping routines - * - * Copyright (C) 2002 Maas Digital LLC - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#include -#include -#include -#include -#include - -#include "dma.h" - -/* dma_prog_region */ - -void dma_prog_region_init(struct dma_prog_region *prog) -{ - prog->kvirt = NULL; - prog->dev = NULL; - prog->n_pages = 0; - prog->bus_addr = 0; -} - -int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes, - struct pci_dev *dev) -{ - /* round up to page size */ - n_bytes = PAGE_ALIGN(n_bytes); - - prog->n_pages = n_bytes >> PAGE_SHIFT; - - prog->kvirt = pci_alloc_consistent(dev, n_bytes, &prog->bus_addr); - if (!prog->kvirt) { - printk(KERN_ERR - "dma_prog_region_alloc: pci_alloc_consistent() failed\n"); - dma_prog_region_free(prog); - return -ENOMEM; - } - - prog->dev = dev; - - return 0; -} - -void dma_prog_region_free(struct dma_prog_region *prog) -{ - if (prog->kvirt) { - pci_free_consistent(prog->dev, prog->n_pages << PAGE_SHIFT, - prog->kvirt, prog->bus_addr); - } - - prog->kvirt = NULL; - prog->dev = NULL; - prog->n_pages = 0; - prog->bus_addr = 0; -} - -/* dma_region */ - -/** - * dma_region_init - clear out all fields but do not allocate anything - */ -void dma_region_init(struct dma_region *dma) -{ - dma->kvirt = NULL; - dma->dev = NULL; - dma->n_pages = 0; - dma->n_dma_pages = 0; - dma->sglist = NULL; -} - -/** - * dma_region_alloc - allocate the buffer and map it to the IOMMU - */ -int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, - struct pci_dev *dev, int direction) -{ - unsigned int i; - - /* round up to page size */ - n_bytes = PAGE_ALIGN(n_bytes); - - dma->n_pages = n_bytes >> PAGE_SHIFT; - - dma->kvirt = vmalloc_32(n_bytes); - if (!dma->kvirt) { - printk(KERN_ERR "dma_region_alloc: vmalloc_32() failed\n"); - goto err; - } - - /* Clear the ram out, no junk to the user */ - memset(dma->kvirt, 0, n_bytes); - - /* allocate scatter/gather list */ - dma->sglist = vmalloc(dma->n_pages * sizeof(*dma->sglist)); - if (!dma->sglist) { - printk(KERN_ERR "dma_region_alloc: vmalloc(sglist) failed\n"); - goto err; - } - - sg_init_table(dma->sglist, dma->n_pages); - - /* fill scatter/gather list with pages */ - for (i = 0; i < dma->n_pages; i++) { - unsigned long va = - (unsigned long)dma->kvirt + (i << PAGE_SHIFT); - - sg_set_page(&dma->sglist[i], vmalloc_to_page((void *)va), - PAGE_SIZE, 0); - } - - /* map sglist to the IOMMU */ - dma->n_dma_pages = - pci_map_sg(dev, dma->sglist, dma->n_pages, direction); - - if (dma->n_dma_pages == 0) { - printk(KERN_ERR "dma_region_alloc: pci_map_sg() failed\n"); - goto err; - } - - dma->dev = dev; - dma->direction = direction; - - return 0; - - err: - dma_region_free(dma); - return -ENOMEM; -} - -/** - * dma_region_free - unmap and free the buffer - */ -void dma_region_free(struct dma_region *dma) -{ - if (dma->n_dma_pages) { - pci_unmap_sg(dma->dev, dma->sglist, dma->n_pages, - dma->direction); - dma->n_dma_pages = 0; - dma->dev = NULL; - } - - vfree(dma->sglist); - dma->sglist = NULL; - - vfree(dma->kvirt); - dma->kvirt = NULL; - dma->n_pages = 0; -} - -/* find the scatterlist index and remaining offset corresponding to a - given offset from the beginning of the buffer */ -static inline int dma_region_find(struct dma_region *dma, unsigned long offset, - unsigned int start, unsigned long *rem) -{ - int i; - unsigned long off = offset; - - for (i = start; i < dma->n_dma_pages; i++) { - if (off < sg_dma_len(&dma->sglist[i])) { - *rem = off; - break; - } - - off -= sg_dma_len(&dma->sglist[i]); - } - - BUG_ON(i >= dma->n_dma_pages); - - return i; -} - -/** - * dma_region_offset_to_bus - get bus address of an offset within a DMA region - * - * Returns the DMA bus address of the byte with the given @offset relative to - * the beginning of the @dma. - */ -dma_addr_t dma_region_offset_to_bus(struct dma_region * dma, - unsigned long offset) -{ - unsigned long rem = 0; - - struct scatterlist *sg = - &dma->sglist[dma_region_find(dma, offset, 0, &rem)]; - return sg_dma_address(sg) + rem; -} - -/** - * dma_region_sync_for_cpu - sync the CPU's view of the buffer - */ -void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, - unsigned long len) -{ - int first, last; - unsigned long rem = 0; - - if (!len) - len = 1; - - first = dma_region_find(dma, offset, 0, &rem); - last = dma_region_find(dma, rem + len - 1, first, &rem); - - pci_dma_sync_sg_for_cpu(dma->dev, &dma->sglist[first], last - first + 1, - dma->direction); -} - -/** - * dma_region_sync_for_device - sync the IO bus' view of the buffer - */ -void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, - unsigned long len) -{ - int first, last; - unsigned long rem = 0; - - if (!len) - len = 1; - - first = dma_region_find(dma, offset, 0, &rem); - last = dma_region_find(dma, rem + len - 1, first, &rem); - - pci_dma_sync_sg_for_device(dma->dev, &dma->sglist[first], - last - first + 1, dma->direction); -} - -#ifdef CONFIG_MMU - -static int dma_region_pagefault(struct vm_area_struct *vma, - struct vm_fault *vmf) -{ - struct dma_region *dma = (struct dma_region *)vma->vm_private_data; - - if (!dma->kvirt) - return VM_FAULT_SIGBUS; - - if (vmf->pgoff >= dma->n_pages) - return VM_FAULT_SIGBUS; - - vmf->page = vmalloc_to_page(dma->kvirt + (vmf->pgoff << PAGE_SHIFT)); - get_page(vmf->page); - return 0; -} - -static const struct vm_operations_struct dma_region_vm_ops = { - .fault = dma_region_pagefault, -}; - -/** - * dma_region_mmap - map the buffer into a user space process - */ -int dma_region_mmap(struct dma_region *dma, struct file *file, - struct vm_area_struct *vma) -{ - unsigned long size; - - if (!dma->kvirt) - return -EINVAL; - - /* must be page-aligned (XXX: comment is wrong, we could allow pgoff) */ - if (vma->vm_pgoff != 0) - return -EINVAL; - - /* check the length */ - size = vma->vm_end - vma->vm_start; - if (size > (dma->n_pages << PAGE_SHIFT)) - return -EINVAL; - - vma->vm_ops = &dma_region_vm_ops; - vma->vm_private_data = dma; - vma->vm_file = file; - vma->vm_flags |= VM_RESERVED | VM_ALWAYSDUMP; - - return 0; -} - -#else /* CONFIG_MMU */ - -int dma_region_mmap(struct dma_region *dma, struct file *file, - struct vm_area_struct *vma) -{ - return -EINVAL; -} - -#endif /* CONFIG_MMU */ diff --git a/drivers/ieee1394/dma.h b/drivers/ieee1394/dma.h deleted file mode 100644 index 467373cab8e5..000000000000 --- a/drivers/ieee1394/dma.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * DMA region bookkeeping routines - * - * Copyright (C) 2002 Maas Digital LLC - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#ifndef IEEE1394_DMA_H -#define IEEE1394_DMA_H - -#include - -struct file; -struct pci_dev; -struct scatterlist; -struct vm_area_struct; - -/** - * struct dma_prog_region - small contiguous DMA buffer - * @kvirt: kernel virtual address - * @dev: PCI device - * @n_pages: number of kernel pages - * @bus_addr: base bus address - * - * a small, physically contiguous DMA buffer with random-access, synchronous - * usage characteristics - */ -struct dma_prog_region { - unsigned char *kvirt; - struct pci_dev *dev; - unsigned int n_pages; - dma_addr_t bus_addr; -}; - -/* clear out all fields but do not allocate any memory */ -void dma_prog_region_init(struct dma_prog_region *prog); -int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes, - struct pci_dev *dev); -void dma_prog_region_free(struct dma_prog_region *prog); - -static inline dma_addr_t dma_prog_region_offset_to_bus( - struct dma_prog_region *prog, unsigned long offset) -{ - return prog->bus_addr + offset; -} - -/** - * struct dma_region - large non-contiguous DMA buffer - * @virt: kernel virtual address - * @dev: PCI device - * @n_pages: number of kernel pages - * @n_dma_pages: number of IOMMU pages - * @sglist: IOMMU mapping - * @direction: PCI_DMA_TODEVICE, etc. - * - * a large, non-physically-contiguous DMA buffer with streaming, asynchronous - * usage characteristics - */ -struct dma_region { - unsigned char *kvirt; - struct pci_dev *dev; - unsigned int n_pages; - unsigned int n_dma_pages; - struct scatterlist *sglist; - int direction; -}; - -void dma_region_init(struct dma_region *dma); -int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, - struct pci_dev *dev, int direction); -void dma_region_free(struct dma_region *dma); -void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, - unsigned long len); -void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, - unsigned long len); -int dma_region_mmap(struct dma_region *dma, struct file *file, - struct vm_area_struct *vma); -dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, - unsigned long offset); - -/** - * dma_region_i - macro to index into a DMA region (or dma_prog_region) - */ -#define dma_region_i(_dma, _type, _index) \ - ( ((_type*) ((_dma)->kvirt)) + (_index) ) - -#endif /* IEEE1394_DMA_H */ diff --git a/drivers/ieee1394/dv1394-private.h b/drivers/ieee1394/dv1394-private.h deleted file mode 100644 index 18b92cbf4a9f..000000000000 --- a/drivers/ieee1394/dv1394-private.h +++ /dev/null @@ -1,587 +0,0 @@ -/* - * dv1394-private.h - DV input/output over IEEE 1394 on OHCI chips - * Copyright (C)2001 Daniel Maas - * receive by Dan Dennedy - * - * based on: - * video1394.h - driver for OHCI 1394 boards - * Copyright (C)1999,2000 Sebastien Rougeaux - * Peter Schlaile - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _DV_1394_PRIVATE_H -#define _DV_1394_PRIVATE_H - -#include "ieee1394.h" -#include "ohci1394.h" -#include "dma.h" - -/* data structures private to the dv1394 driver */ -/* none of this is exposed to user-space */ - - -/* - the 8-byte CIP (Common Isochronous Packet) header that precedes - each packet of DV data. - - See the IEC 61883 standard. -*/ - -struct CIP_header { unsigned char b[8]; }; - -static inline void fill_cip_header(struct CIP_header *cip, - unsigned char source_node_id, - unsigned long counter, - enum pal_or_ntsc format, - unsigned long timestamp) -{ - cip->b[0] = source_node_id; - cip->b[1] = 0x78; /* packet size in quadlets (480/4) - even for empty packets! */ - cip->b[2] = 0x00; - cip->b[3] = counter; - - cip->b[4] = 0x80; /* const */ - - switch(format) { - case DV1394_PAL: - cip->b[5] = 0x80; - break; - case DV1394_NTSC: - cip->b[5] = 0x00; - break; - } - - cip->b[6] = timestamp >> 8; - cip->b[7] = timestamp & 0xFF; -} - - - -/* - DMA commands used to program the OHCI's DMA engine - - See the Texas Instruments OHCI 1394 chipset documentation. -*/ - -struct output_more_immediate { __le32 q[8]; }; -struct output_more { __le32 q[4]; }; -struct output_last { __le32 q[4]; }; -struct input_more { __le32 q[4]; }; -struct input_last { __le32 q[4]; }; - -/* outputs */ - -static inline void fill_output_more_immediate(struct output_more_immediate *omi, - unsigned char tag, - unsigned char channel, - unsigned char sync_tag, - unsigned int payload_size) -{ - omi->q[0] = cpu_to_le32(0x02000000 | 8); /* OUTPUT_MORE_IMMEDIATE; 8 is the size of the IT header */ - omi->q[1] = cpu_to_le32(0); - omi->q[2] = cpu_to_le32(0); - omi->q[3] = cpu_to_le32(0); - - /* IT packet header */ - omi->q[4] = cpu_to_le32( (0x0 << 16) /* IEEE1394_SPEED_100 */ - | (tag << 14) - | (channel << 8) - | (TCODE_ISO_DATA << 4) - | (sync_tag) ); - - /* reserved field; mimic behavior of my Sony DSR-40 */ - omi->q[5] = cpu_to_le32((payload_size << 16) | (0x7F << 8) | 0xA0); - - omi->q[6] = cpu_to_le32(0); - omi->q[7] = cpu_to_le32(0); -} - -static inline void fill_output_more(struct output_more *om, - unsigned int data_size, - unsigned long data_phys_addr) -{ - om->q[0] = cpu_to_le32(data_size); - om->q[1] = cpu_to_le32(data_phys_addr); - om->q[2] = cpu_to_le32(0); - om->q[3] = cpu_to_le32(0); -} - -static inline void fill_output_last(struct output_last *ol, - int want_timestamp, - int want_interrupt, - unsigned int data_size, - unsigned long data_phys_addr) -{ - u32 temp = 0; - temp |= 1 << 28; /* OUTPUT_LAST */ - - if (want_timestamp) /* controller will update timestamp at DMA time */ - temp |= 1 << 27; - - if (want_interrupt) - temp |= 3 << 20; - - temp |= 3 << 18; /* must take branch */ - temp |= data_size; - - ol->q[0] = cpu_to_le32(temp); - ol->q[1] = cpu_to_le32(data_phys_addr); - ol->q[2] = cpu_to_le32(0); - ol->q[3] = cpu_to_le32(0); -} - -/* inputs */ - -static inline void fill_input_more(struct input_more *im, - int want_interrupt, - unsigned int data_size, - unsigned long data_phys_addr) -{ - u32 temp = 2 << 28; /* INPUT_MORE */ - temp |= 8 << 24; /* s = 1, update xferStatus and resCount */ - if (want_interrupt) - temp |= 0 << 20; /* interrupts, i=0 in packet-per-buffer mode */ - temp |= 0x0 << 16; /* disable branch to address for packet-per-buffer mode */ - /* disable wait on sync field, not used in DV :-( */ - temp |= data_size; - - im->q[0] = cpu_to_le32(temp); - im->q[1] = cpu_to_le32(data_phys_addr); - im->q[2] = cpu_to_le32(0); /* branchAddress and Z not use in packet-per-buffer mode */ - im->q[3] = cpu_to_le32(0); /* xferStatus & resCount, resCount must be initialize to data_size */ -} - -static inline void fill_input_last(struct input_last *il, - int want_interrupt, - unsigned int data_size, - unsigned long data_phys_addr) -{ - u32 temp = 3 << 28; /* INPUT_LAST */ - temp |= 8 << 24; /* s = 1, update xferStatus and resCount */ - if (want_interrupt) - temp |= 3 << 20; /* enable interrupts */ - temp |= 0xC << 16; /* enable branch to address */ - /* disable wait on sync field, not used in DV :-( */ - temp |= data_size; - - il->q[0] = cpu_to_le32(temp); - il->q[1] = cpu_to_le32(data_phys_addr); - il->q[2] = cpu_to_le32(1); /* branchAddress (filled in later) and Z = 1 descriptor in next block */ - il->q[3] = cpu_to_le32(data_size); /* xferStatus & resCount, resCount must be initialize to data_size */ -} - - - -/* - A "DMA descriptor block" consists of several contiguous DMA commands. - struct DMA_descriptor_block encapsulates all of the commands necessary - to send one packet of DV data. - - There are three different types of these blocks: - - 1) command to send an empty packet (CIP header only, no DV data): - - OUTPUT_MORE-Immediate <-- contains the iso header in-line - OUTPUT_LAST <-- points to the CIP header - - 2) command to send a full packet when the DV data payload does NOT - cross a page boundary: - - OUTPUT_MORE-Immediate <-- contains the iso header in-line - OUTPUT_MORE <-- points to the CIP header - OUTPUT_LAST <-- points to entire DV data payload - - 3) command to send a full packet when the DV payload DOES cross - a page boundary: - - OUTPUT_MORE-Immediate <-- contains the iso header in-line - OUTPUT_MORE <-- points to the CIP header - OUTPUT_MORE <-- points to first part of DV data payload - OUTPUT_LAST <-- points to second part of DV data payload - - This struct describes all three block types using unions. - - !!! It is vital that an even number of these descriptor blocks fit on one - page of memory, since a block cannot cross a page boundary !!! - - */ - -struct DMA_descriptor_block { - - union { - struct { - /* iso header, common to all output block types */ - struct output_more_immediate omi; - - union { - /* empty packet */ - struct { - struct output_last ol; /* CIP header */ - } empty; - - /* full packet */ - struct { - struct output_more om; /* CIP header */ - - union { - /* payload does not cross page boundary */ - struct { - struct output_last ol; /* data payload */ - } nocross; - - /* payload crosses page boundary */ - struct { - struct output_more om; /* data payload */ - struct output_last ol; /* data payload */ - } cross; - } u; - - } full; - } u; - } out; - - struct { - struct input_last il; - } in; - - } u; - - /* ensure that PAGE_SIZE % sizeof(struct DMA_descriptor_block) == 0 - by padding out to 128 bytes */ - u32 __pad__[12]; -}; - - -/* struct frame contains all data associated with one frame in the - ringbuffer these are allocated when the DMA context is initialized - do_dv1394_init(). They are re-used after the card finishes - transmitting the frame. */ - -struct video_card; /* forward declaration */ - -struct frame { - - /* points to the struct video_card that owns this frame */ - struct video_card *video; - - /* index of this frame in video_card->frames[] */ - unsigned int frame_num; - - /* FRAME_CLEAR - DMA program not set up, waiting for data - FRAME_READY - DMA program written, ready to transmit - - Changes to these should be locked against the interrupt - */ - enum { - FRAME_CLEAR = 0, - FRAME_READY - } state; - - /* whether this frame has been DMA'ed already; used only from - the IRQ handler to determine whether the frame can be reset */ - int done; - - - /* kernel virtual pointer to the start of this frame's data in - the user ringbuffer. Use only for CPU access; to get the DMA - bus address you must go through the video->user_dma mapping */ - unsigned long data; - - /* Max # of packets per frame */ -#define MAX_PACKETS 500 - - - /* a PAGE_SIZE memory pool for allocating CIP headers - !header_pool must be aligned to PAGE_SIZE! */ - struct CIP_header *header_pool; - dma_addr_t header_pool_dma; - - - /* a physically contiguous memory pool for allocating DMA - descriptor blocks; usually around 64KB in size - !descriptor_pool must be aligned to PAGE_SIZE! */ - struct DMA_descriptor_block *descriptor_pool; - dma_addr_t descriptor_pool_dma; - unsigned long descriptor_pool_size; - - - /* # of packets allocated for this frame */ - unsigned int n_packets; - - - /* below are several pointers (kernel virtual addresses, not - DMA bus addresses) to parts of the DMA program. These are - set each time the DMA program is written in - frame_prepare(). They are used later on, e.g. from the - interrupt handler, to check the status of the frame */ - - /* points to status/timestamp field of first DMA packet */ - /* (we'll check it later to monitor timestamp accuracy) */ - __le32 *frame_begin_timestamp; - - /* the timestamp we assigned to the first packet in the frame */ - u32 assigned_timestamp; - - /* pointer to the first packet's CIP header (where the timestamp goes) */ - struct CIP_header *cip_syt1; - - /* pointer to the second packet's CIP header - (only set if the first packet was empty) */ - struct CIP_header *cip_syt2; - - /* in order to figure out what caused an interrupt, - store pointers to the status fields of the two packets - that can cause interrupts. We'll check these from the - interrupt handler. - */ - __le32 *mid_frame_timestamp; - __le32 *frame_end_timestamp; - - /* branch address field of final packet. This is effectively - the "tail" in the chain of DMA descriptor blocks. - We will fill it with the address of the first DMA descriptor - block in the subsequent frame, once it is ready. - */ - __le32 *frame_end_branch; - - /* the number of descriptors in the first descriptor block - of the frame. Needed to start DMA */ - int first_n_descriptors; -}; - - -struct packet { - __le16 timestamp; - u16 invalid; - u16 iso_header; - __le16 data_length; - u32 cip_h1; - u32 cip_h2; - unsigned char data[480]; - unsigned char padding[16]; /* force struct size =512 for page alignment */ -}; - - -/* allocate/free a frame */ -static struct frame* frame_new(unsigned int frame_num, struct video_card *video); -static void frame_delete(struct frame *f); - -/* reset f so that it can be used again */ -static void frame_reset(struct frame *f); - -/* struct video_card contains all data associated with one instance - of the dv1394 driver -*/ -enum modes { - MODE_RECEIVE, - MODE_TRANSMIT -}; - -struct video_card { - - /* ohci card to which this instance corresponds */ - struct ti_ohci *ohci; - - /* OHCI card id; the link between the VFS inode and a specific video_card - (essentially the device minor number) */ - int id; - - /* entry in dv1394_cards */ - struct list_head list; - - /* OHCI card IT DMA context number, -1 if not in use */ - int ohci_it_ctx; - struct ohci1394_iso_tasklet it_tasklet; - - /* register offsets for current IT DMA context, 0 if not in use */ - u32 ohci_IsoXmitContextControlSet; - u32 ohci_IsoXmitContextControlClear; - u32 ohci_IsoXmitCommandPtr; - - /* OHCI card IR DMA context number, -1 if not in use */ - struct ohci1394_iso_tasklet ir_tasklet; - int ohci_ir_ctx; - - /* register offsets for current IR DMA context, 0 if not in use */ - u32 ohci_IsoRcvContextControlSet; - u32 ohci_IsoRcvContextControlClear; - u32 ohci_IsoRcvCommandPtr; - u32 ohci_IsoRcvContextMatch; - - - /* CONCURRENCY CONTROL */ - - /* there are THREE levels of locking associated with video_card. */ - - /* - 1) the 'open' flag - this prevents more than one process from - opening the device. (the driver currently assumes only one opener). - This is a regular int, but use test_and_set_bit() (on bit zero) - for atomicity. - */ - unsigned long open; - - /* - 2) the spinlock - this provides mutual exclusion between the interrupt - handler and process-context operations. Generally you must take the - spinlock under the following conditions: - 1) DMA (and hence the interrupt handler) may be running - AND - 2) you need to operate on the video_card, especially active_frame - - It is OK to play with video_card without taking the spinlock if - you are certain that DMA is not running. Even if DMA is running, - it is OK to *read* active_frame with the lock, then drop it - immediately. This is safe because the interrupt handler will never - advance active_frame onto a frame that is not READY (and the spinlock - must be held while marking a frame READY). - - spinlock is also used to protect ohci_it_ctx and ohci_ir_ctx, - which can be accessed from both process and interrupt context - */ - spinlock_t spinlock; - - /* flag to prevent spurious interrupts (which OHCI seems to - generate a lot :) from accessing the struct */ - int dma_running; - - /* - 3) the sleeping mutex 'mtx' - this is used from process context only, - to serialize various operations on the video_card. Even though only one - open() is allowed, we still need to prevent multiple threads of execution - from entering calls like read, write, ioctl, etc. - - I honestly can't think of a good reason to use dv1394 from several threads - at once, but we need to serialize anyway to prevent oopses =). - - NOTE: if you need both spinlock and mtx, take mtx first to avoid deadlock! - */ - struct mutex mtx; - - /* people waiting for buffer space, please form a line here... */ - wait_queue_head_t waitq; - - /* support asynchronous I/O signals (SIGIO) */ - struct fasync_struct *fasync; - - /* the large, non-contiguous (rvmalloc()) ringbuffer for DV - data, exposed to user-space via mmap() */ - unsigned long dv_buf_size; - struct dma_region dv_buf; - - /* next byte in the ringbuffer that a write() call will fill */ - size_t write_off; - - struct frame *frames[DV1394_MAX_FRAMES]; - - /* n_frames also serves as an indicator that this struct video_card is - initialized and ready to run DMA buffers */ - - int n_frames; - - /* this is the frame that is currently "owned" by the OHCI DMA controller - (set to -1 iff DMA is not running) - - ! must lock against the interrupt handler when accessing it ! - - RULES: - - Only the interrupt handler may change active_frame if DMA - is running; if not, process may change it - - If the next frame is READY, the interrupt handler will advance - active_frame when the current frame is finished. - - If the next frame is CLEAR, the interrupt handler will re-transmit - the current frame, and the dropped_frames counter will be incremented. - - The interrupt handler will NEVER advance active_frame to a - frame that is not READY. - */ - int active_frame; - int first_run; - - /* the same locking rules apply to these three fields also: */ - - /* altered ONLY from process context. Must check first_clear_frame->state; - if it's READY, that means the ringbuffer is full with READY frames; - if it's CLEAR, that means one or more ringbuffer frames are CLEAR */ - unsigned int first_clear_frame; - - /* altered both by process and interrupt */ - unsigned int n_clear_frames; - - /* only altered by the interrupt */ - unsigned int dropped_frames; - - - - /* the CIP accumulator and continuity counter are properties - of the DMA stream as a whole (not a single frame), so they - are stored here in the video_card */ - - unsigned long cip_accum; - unsigned long cip_n, cip_d; - unsigned int syt_offset; - unsigned int continuity_counter; - - enum pal_or_ntsc pal_or_ntsc; - - /* redundant, but simplifies the code somewhat */ - unsigned int frame_size; /* in bytes */ - - /* the isochronous channel to use, -1 if video card is inactive */ - int channel; - - - /* physically contiguous packet ringbuffer for receive */ - struct dma_region packet_buf; - unsigned long packet_buf_size; - - unsigned int current_packet; - int first_frame; /* received first start frame marker? */ - enum modes mode; -}; - -/* - if the video_card is not initialized, then the ONLY fields that are valid are: - ohci - open - n_frames -*/ - -static inline int video_card_initialized(struct video_card *v) -{ - return v->n_frames > 0; -} - -static int do_dv1394_init(struct video_card *video, struct dv1394_init *init); -static int do_dv1394_init_default(struct video_card *video); -static void do_dv1394_shutdown(struct video_card *video, int free_user_buf); - - -/* NTSC empty packet rate accurate to within 0.01%, - calibrated against a Sony DSR-40 DVCAM deck */ - -#define CIP_N_NTSC 68000000 -#define CIP_D_NTSC 1068000000 - -#define CIP_N_PAL 1 -#define CIP_D_PAL 16 - -#endif /* _DV_1394_PRIVATE_H */ - diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c deleted file mode 100644 index c5a031b79d03..000000000000 --- a/drivers/ieee1394/dv1394.c +++ /dev/null @@ -1,2584 +0,0 @@ -/* - * dv1394.c - DV input/output over IEEE 1394 on OHCI chips - * Copyright (C)2001 Daniel Maas - * receive by Dan Dennedy - * - * based on: - * video1394.c - video driver for OHCI 1394 boards - * Copyright (C)1999,2000 Sebastien Rougeaux - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - OVERVIEW - - I designed dv1394 as a "pipe" that you can use to shoot DV onto a - FireWire bus. In transmission mode, dv1394 does the following: - - 1. accepts contiguous frames of DV data from user-space, via write() - or mmap() (see dv1394.h for the complete API) - 2. wraps IEC 61883 packets around the DV data, inserting - empty synchronization packets as necessary - 3. assigns accurate SYT timestamps to the outgoing packets - 4. shoots them out using the OHCI card's IT DMA engine - - Thanks to Dan Dennedy, we now have a receive mode that does the following: - - 1. accepts raw IEC 61883 packets from the OHCI card - 2. re-assembles the DV data payloads into contiguous frames, - discarding empty packets - 3. sends the DV data to user-space via read() or mmap() -*/ - -/* - TODO: - - - tunable frame-drop behavior: either loop last frame, or halt transmission - - - use a scatter/gather buffer for DMA programs (f->descriptor_pool) - so that we don't rely on allocating 64KB of contiguous kernel memory - via pci_alloc_consistent() - - DONE: - - during reception, better handling of dropped frames and continuity errors - - during reception, prevent DMA from bypassing the irq tasklets - - reduce irq rate during reception (1/250 packets). - - add many more internal buffers during reception with scatter/gather dma. - - add dbc (continuity) checking on receive, increment status.dropped_frames - if not continuous. - - restart IT DMA after a bus reset - - safely obtain and release ISO Tx channels in cooperation with OHCI driver - - map received DIF blocks to their proper location in DV frame (ensure - recovery if dropped packet) - - handle bus resets gracefully (OHCI card seems to take care of this itself(!)) - - do not allow resizing the user_buf once allocated; eliminate nuke_buffer_mappings - - eliminated #ifdef DV1394_DEBUG_LEVEL by inventing macros debug_printk and irq_printk - - added wmb() and mb() to places where PCI read/write ordering needs to be enforced - - set video->id correctly - - store video_cards in an array indexed by OHCI card ID, rather than a list - - implement DMA context allocation to cooperate with other users of the OHCI - - fix all XXX showstoppers - - disable IR/IT DMA interrupts on shutdown - - flush pci writes to the card by issuing a read - - character device dispatching - - switch over to the new kernel DMA API (pci_map_*()) (* needs testing on platforms with IOMMU!) - - keep all video_cards in a list (for open() via chardev), set file->private_data = video - - dv1394_poll should indicate POLLIN when receiving buffers are available - - add proc fs interface to set cip_n, cip_d, syt_offset, and video signal - - expose xmit and recv as separate devices (not exclusive) - - expose NTSC and PAL as separate devices (can be overridden) - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dv1394.h" -#include "dv1394-private.h" -#include "highlevel.h" -#include "hosts.h" -#include "ieee1394.h" -#include "ieee1394_core.h" -#include "ieee1394_hotplug.h" -#include "ieee1394_types.h" -#include "nodemgr.h" -#include "ohci1394.h" - -/* DEBUG LEVELS: - 0 - no debugging messages - 1 - some debugging messages, but none during DMA frame transmission - 2 - lots of messages, including during DMA frame transmission - (will cause underflows if your machine is too slow!) -*/ - -#define DV1394_DEBUG_LEVEL 0 - -/* for debugging use ONLY: allow more than one open() of the device */ -/* #define DV1394_ALLOW_MORE_THAN_ONE_OPEN 1 */ - -#if DV1394_DEBUG_LEVEL >= 2 -#define irq_printk( args... ) printk( args ) -#else -#define irq_printk( args... ) do {} while (0) -#endif - -#if DV1394_DEBUG_LEVEL >= 1 -#define debug_printk( args... ) printk( args) -#else -#define debug_printk( args... ) do {} while (0) -#endif - -/* issue a dummy PCI read to force the preceding write - to be posted to the PCI bus immediately */ - -static inline void flush_pci_write(struct ti_ohci *ohci) -{ - mb(); - reg_read(ohci, OHCI1394_IsochronousCycleTimer); -} - -static void it_tasklet_func(unsigned long data); -static void ir_tasklet_func(unsigned long data); - -#ifdef CONFIG_COMPAT -static long dv1394_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg); -#endif - -/* GLOBAL DATA */ - -/* list of all video_cards */ -static LIST_HEAD(dv1394_cards); -static DEFINE_SPINLOCK(dv1394_cards_lock); - -/* translate from a struct file* to the corresponding struct video_card* */ - -static inline struct video_card* file_to_video_card(struct file *file) -{ - return file->private_data; -} - -/*** FRAME METHODS *********************************************************/ - -static void frame_reset(struct frame *f) -{ - f->state = FRAME_CLEAR; - f->done = 0; - f->n_packets = 0; - f->frame_begin_timestamp = NULL; - f->assigned_timestamp = 0; - f->cip_syt1 = NULL; - f->cip_syt2 = NULL; - f->mid_frame_timestamp = NULL; - f->frame_end_timestamp = NULL; - f->frame_end_branch = NULL; -} - -static struct frame* frame_new(unsigned int frame_num, struct video_card *video) -{ - struct frame *f = kmalloc(sizeof(*f), GFP_KERNEL); - if (!f) - return NULL; - - f->video = video; - f->frame_num = frame_num; - - f->header_pool = pci_alloc_consistent(f->video->ohci->dev, PAGE_SIZE, &f->header_pool_dma); - if (!f->header_pool) { - printk(KERN_ERR "dv1394: failed to allocate CIP header pool\n"); - kfree(f); - return NULL; - } - - debug_printk("dv1394: frame_new: allocated CIP header pool at virt 0x%08lx (contig) dma 0x%08lx size %ld\n", - (unsigned long) f->header_pool, (unsigned long) f->header_pool_dma, PAGE_SIZE); - - f->descriptor_pool_size = MAX_PACKETS * sizeof(struct DMA_descriptor_block); - /* make it an even # of pages */ - f->descriptor_pool_size += PAGE_SIZE - (f->descriptor_pool_size%PAGE_SIZE); - - f->descriptor_pool = pci_alloc_consistent(f->video->ohci->dev, - f->descriptor_pool_size, - &f->descriptor_pool_dma); - if (!f->descriptor_pool) { - pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma); - kfree(f); - return NULL; - } - - debug_printk("dv1394: frame_new: allocated DMA program memory at virt 0x%08lx (contig) dma 0x%08lx size %ld\n", - (unsigned long) f->descriptor_pool, (unsigned long) f->descriptor_pool_dma, f->descriptor_pool_size); - - f->data = 0; - frame_reset(f); - - return f; -} - -static void frame_delete(struct frame *f) -{ - pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma); - pci_free_consistent(f->video->ohci->dev, f->descriptor_pool_size, f->descriptor_pool, f->descriptor_pool_dma); - kfree(f); -} - - - - -/* - frame_prepare() - build the DMA program for transmitting - - Frame_prepare() must be called OUTSIDE the video->spinlock. - However, frame_prepare() must still be serialized, so - it should be called WITH the video->mtx taken. - */ - -static void frame_prepare(struct video_card *video, unsigned int this_frame) -{ - struct frame *f = video->frames[this_frame]; - int last_frame; - - struct DMA_descriptor_block *block; - dma_addr_t block_dma; - struct CIP_header *cip; - dma_addr_t cip_dma; - - unsigned int n_descriptors, full_packets, packets_per_frame, payload_size; - - /* these flags denote packets that need special attention */ - int empty_packet, first_packet, last_packet, mid_packet; - - __le32 *branch_address, *last_branch_address = NULL; - unsigned long data_p; - int first_packet_empty = 0; - u32 cycleTimer, ct_sec, ct_cyc, ct_off; - unsigned long irq_flags; - - irq_printk("frame_prepare( %d ) ---------------------\n", this_frame); - - full_packets = 0; - - - - if (video->pal_or_ntsc == DV1394_PAL) - packets_per_frame = DV1394_PAL_PACKETS_PER_FRAME; - else - packets_per_frame = DV1394_NTSC_PACKETS_PER_FRAME; - - while ( full_packets < packets_per_frame ) { - empty_packet = first_packet = last_packet = mid_packet = 0; - - data_p = f->data + full_packets * 480; - - /************************************************/ - /* allocate a descriptor block and a CIP header */ - /************************************************/ - - /* note: these should NOT cross a page boundary (DMA restriction) */ - - if (f->n_packets >= MAX_PACKETS) { - printk(KERN_ERR "dv1394: FATAL ERROR: max packet count exceeded\n"); - return; - } - - /* the block surely won't cross a page boundary, - since an even number of descriptor_blocks fit on a page */ - block = &(f->descriptor_pool[f->n_packets]); - - /* DMA address of the block = offset of block relative - to the kernel base address of the descriptor pool - + DMA base address of the descriptor pool */ - block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma; - - - /* the whole CIP pool fits on one page, so no worries about boundaries */ - if ( ((unsigned long) &(f->header_pool[f->n_packets]) - (unsigned long) f->header_pool) - > PAGE_SIZE) { - printk(KERN_ERR "dv1394: FATAL ERROR: no room to allocate CIP header\n"); - return; - } - - cip = &(f->header_pool[f->n_packets]); - - /* DMA address of the CIP header = offset of cip - relative to kernel base address of the header pool - + DMA base address of the header pool */ - cip_dma = (unsigned long) cip % PAGE_SIZE + f->header_pool_dma; - - /* is this an empty packet? */ - - if (video->cip_accum > (video->cip_d - video->cip_n)) { - empty_packet = 1; - payload_size = 8; - video->cip_accum -= (video->cip_d - video->cip_n); - } else { - payload_size = 488; - video->cip_accum += video->cip_n; - } - - /* there are three important packets each frame: - - the first packet in the frame - we ask the card to record the timestamp when - this packet is actually sent, so we can monitor - how accurate our timestamps are. Also, the first - packet serves as a semaphore to let us know that - it's OK to free the *previous* frame's DMA buffer - - the last packet in the frame - this packet is used to detect buffer underflows. - if this is the last ready frame, the last DMA block - will have a branch back to the beginning of the frame - (so that the card will re-send the frame on underflow). - if this branch gets taken, we know that at least one - frame has been dropped. When the next frame is ready, - the branch is pointed to its first packet, and the - semaphore is disabled. - - a "mid" packet slightly before the end of the frame - this packet should trigger - an interrupt so we can go and assign a timestamp to the first packet - in the next frame. We don't use the very last packet in the frame - for this purpose, because that would leave very little time to set - the timestamp before DMA starts on the next frame. - */ - - if (f->n_packets == 0) { - first_packet = 1; - } else if ( full_packets == (packets_per_frame-1) ) { - last_packet = 1; - } else if (f->n_packets == packets_per_frame) { - mid_packet = 1; - } - - - /********************/ - /* setup CIP header */ - /********************/ - - /* the timestamp will be written later from the - mid-frame interrupt handler. For now we just - store the address of the CIP header(s) that - need a timestamp. */ - - /* first packet in the frame needs a timestamp */ - if (first_packet) { - f->cip_syt1 = cip; - if (empty_packet) - first_packet_empty = 1; - - } else if (first_packet_empty && (f->n_packets == 1) ) { - /* if the first packet was empty, the second - packet's CIP header also needs a timestamp */ - f->cip_syt2 = cip; - } - - fill_cip_header(cip, - /* the node ID number of the OHCI card */ - reg_read(video->ohci, OHCI1394_NodeID) & 0x3F, - video->continuity_counter, - video->pal_or_ntsc, - 0xFFFF /* the timestamp is filled in later */); - - /* advance counter, only for full packets */ - if ( ! empty_packet ) - video->continuity_counter++; - - /******************************/ - /* setup DMA descriptor block */ - /******************************/ - - /* first descriptor - OUTPUT_MORE_IMMEDIATE, for the controller's IT header */ - fill_output_more_immediate( &(block->u.out.omi), 1, video->channel, 0, payload_size); - - if (empty_packet) { - /* second descriptor - OUTPUT_LAST for CIP header */ - fill_output_last( &(block->u.out.u.empty.ol), - - /* want completion status on all interesting packets */ - (first_packet || mid_packet || last_packet) ? 1 : 0, - - /* want interrupts on all interesting packets */ - (first_packet || mid_packet || last_packet) ? 1 : 0, - - sizeof(struct CIP_header), /* data size */ - cip_dma); - - if (first_packet) - f->frame_begin_timestamp = &(block->u.out.u.empty.ol.q[3]); - else if (mid_packet) - f->mid_frame_timestamp = &(block->u.out.u.empty.ol.q[3]); - else if (last_packet) { - f->frame_end_timestamp = &(block->u.out.u.empty.ol.q[3]); - f->frame_end_branch = &(block->u.out.u.empty.ol.q[2]); - } - - branch_address = &(block->u.out.u.empty.ol.q[2]); - n_descriptors = 3; - if (first_packet) - f->first_n_descriptors = n_descriptors; - - } else { /* full packet */ - - /* second descriptor - OUTPUT_MORE for CIP header */ - fill_output_more( &(block->u.out.u.full.om), - sizeof(struct CIP_header), /* data size */ - cip_dma); - - - /* third (and possibly fourth) descriptor - for DV data */ - /* the 480-byte payload can cross a page boundary; if so, - we need to split it into two DMA descriptors */ - - /* does the 480-byte data payload cross a page boundary? */ - if ( (PAGE_SIZE- ((unsigned long)data_p % PAGE_SIZE) ) < 480 ) { - - /* page boundary crossed */ - - fill_output_more( &(block->u.out.u.full.u.cross.om), - /* data size - how much of data_p fits on the first page */ - PAGE_SIZE - (data_p % PAGE_SIZE), - - /* DMA address of data_p */ - dma_region_offset_to_bus(&video->dv_buf, - data_p - (unsigned long) video->dv_buf.kvirt)); - - fill_output_last( &(block->u.out.u.full.u.cross.ol), - - /* want completion status on all interesting packets */ - (first_packet || mid_packet || last_packet) ? 1 : 0, - - /* want interrupt on all interesting packets */ - (first_packet || mid_packet || last_packet) ? 1 : 0, - - /* data size - remaining portion of data_p */ - 480 - (PAGE_SIZE - (data_p % PAGE_SIZE)), - - /* DMA address of data_p + PAGE_SIZE - (data_p % PAGE_SIZE) */ - dma_region_offset_to_bus(&video->dv_buf, - data_p + PAGE_SIZE - (data_p % PAGE_SIZE) - (unsigned long) video->dv_buf.kvirt)); - - if (first_packet) - f->frame_begin_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]); - else if (mid_packet) - f->mid_frame_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]); - else if (last_packet) { - f->frame_end_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]); - f->frame_end_branch = &(block->u.out.u.full.u.cross.ol.q[2]); - } - - branch_address = &(block->u.out.u.full.u.cross.ol.q[2]); - - n_descriptors = 5; - if (first_packet) - f->first_n_descriptors = n_descriptors; - - full_packets++; - - } else { - /* fits on one page */ - - fill_output_last( &(block->u.out.u.full.u.nocross.ol), - - /* want completion status on all interesting packets */ - (first_packet || mid_packet || last_packet) ? 1 : 0, - - /* want interrupt on all interesting packets */ - (first_packet || mid_packet || last_packet) ? 1 : 0, - - 480, /* data size (480 bytes of DV data) */ - - - /* DMA address of data_p */ - dma_region_offset_to_bus(&video->dv_buf, - data_p - (unsigned long) video->dv_buf.kvirt)); - - if (first_packet) - f->frame_begin_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]); - else if (mid_packet) - f->mid_frame_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]); - else if (last_packet) { - f->frame_end_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]); - f->frame_end_branch = &(block->u.out.u.full.u.nocross.ol.q[2]); - } - - branch_address = &(block->u.out.u.full.u.nocross.ol.q[2]); - - n_descriptors = 4; - if (first_packet) - f->first_n_descriptors = n_descriptors; - - full_packets++; - } - } - - /* link this descriptor block into the DMA program by filling in - the branch address of the previous block */ - - /* note: we are not linked into the active DMA chain yet */ - - if (last_branch_address) { - *(last_branch_address) = cpu_to_le32(block_dma | n_descriptors); - } - - last_branch_address = branch_address; - - - f->n_packets++; - - } - - /* when we first assemble a new frame, set the final branch - to loop back up to the top */ - *(f->frame_end_branch) = cpu_to_le32(f->descriptor_pool_dma | f->first_n_descriptors); - - /* make the latest version of this frame visible to the PCI card */ - dma_region_sync_for_device(&video->dv_buf, f->data - (unsigned long) video->dv_buf.kvirt, video->frame_size); - - /* lock against DMA interrupt */ - spin_lock_irqsave(&video->spinlock, irq_flags); - - f->state = FRAME_READY; - - video->n_clear_frames--; - - last_frame = video->first_clear_frame - 1; - if (last_frame == -1) - last_frame = video->n_frames-1; - - video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames; - - irq_printk(" frame %d prepared, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n last=%d\n", - this_frame, video->active_frame, video->n_clear_frames, video->first_clear_frame, last_frame); - - irq_printk(" begin_ts %08lx mid_ts %08lx end_ts %08lx end_br %08lx\n", - (unsigned long) f->frame_begin_timestamp, - (unsigned long) f->mid_frame_timestamp, - (unsigned long) f->frame_end_timestamp, - (unsigned long) f->frame_end_branch); - - if (video->active_frame != -1) { - - /* if DMA is already active, we are almost done */ - /* just link us onto the active DMA chain */ - if (video->frames[last_frame]->frame_end_branch) { - u32 temp; - - /* point the previous frame's tail to this frame's head */ - *(video->frames[last_frame]->frame_end_branch) = cpu_to_le32(f->descriptor_pool_dma | f->first_n_descriptors); - - /* this write MUST precede the next one, or we could silently drop frames */ - wmb(); - - /* disable the want_status semaphore on the last packet */ - temp = le32_to_cpu(*(video->frames[last_frame]->frame_end_branch - 2)); - temp &= 0xF7CFFFFF; - *(video->frames[last_frame]->frame_end_branch - 2) = cpu_to_le32(temp); - - /* flush these writes to memory ASAP */ - flush_pci_write(video->ohci); - - /* NOTE: - ideally the writes should be "atomic": if - the OHCI card reads the want_status flag in - between them, we'll falsely report a - dropped frame. Hopefully this window is too - small to really matter, and the consequence - is rather harmless. */ - - - irq_printk(" new frame %d linked onto DMA chain\n", this_frame); - - } else { - printk(KERN_ERR "dv1394: last frame not ready???\n"); - } - - } else { - - u32 transmit_sec, transmit_cyc; - u32 ts_cyc; - - /* DMA is stopped, so this is the very first frame */ - video->active_frame = this_frame; - - /* set CommandPtr to address and size of first descriptor block */ - reg_write(video->ohci, video->ohci_IsoXmitCommandPtr, - video->frames[video->active_frame]->descriptor_pool_dma | - f->first_n_descriptors); - - /* assign a timestamp based on the current cycle time... - We'll tell the card to begin DMA 100 cycles from now, - and assign a timestamp 103 cycles from now */ - - cycleTimer = reg_read(video->ohci, OHCI1394_IsochronousCycleTimer); - - ct_sec = cycleTimer >> 25; - ct_cyc = (cycleTimer >> 12) & 0x1FFF; - ct_off = cycleTimer & 0xFFF; - - transmit_sec = ct_sec; - transmit_cyc = ct_cyc + 100; - - transmit_sec += transmit_cyc/8000; - transmit_cyc %= 8000; - - ts_cyc = transmit_cyc + 3; - ts_cyc %= 8000; - - f->assigned_timestamp = (ts_cyc&0xF) << 12; - - /* now actually write the timestamp into the appropriate CIP headers */ - if (f->cip_syt1) { - f->cip_syt1->b[6] = f->assigned_timestamp >> 8; - f->cip_syt1->b[7] = f->assigned_timestamp & 0xFF; - } - if (f->cip_syt2) { - f->cip_syt2->b[6] = f->assigned_timestamp >> 8; - f->cip_syt2->b[7] = f->assigned_timestamp & 0xFF; - } - - /* --- start DMA --- */ - - /* clear all bits in ContextControl register */ - - reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, 0xFFFFFFFF); - wmb(); - - /* the OHCI card has the ability to start ISO transmission on a - particular cycle (start-on-cycle). This way we can ensure that - the first DV frame will have an accurate timestamp. - - However, start-on-cycle only appears to work if the OHCI card - is cycle master! Since the consequences of messing up the first - timestamp are minimal*, just disable start-on-cycle for now. - - * my DV deck drops the first few frames before it "locks in;" - so the first frame having an incorrect timestamp is inconsequential. - */ - -#if 0 - reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, - (1 << 31) /* enable start-on-cycle */ - | ( (transmit_sec & 0x3) << 29) - | (transmit_cyc << 16)); - wmb(); -#endif - - video->dma_running = 1; - - /* set the 'run' bit */ - reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, 0x8000); - flush_pci_write(video->ohci); - - /* --- DMA should be running now --- */ - - debug_printk(" Cycle = %4u ContextControl = %08x CmdPtr = %08x\n", - (reg_read(video->ohci, OHCI1394_IsochronousCycleTimer) >> 12) & 0x1FFF, - reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), - reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)); - - debug_printk(" DMA start - current cycle %4u, transmit cycle %4u (%2u), assigning ts cycle %2u\n", - ct_cyc, transmit_cyc, transmit_cyc & 0xF, ts_cyc & 0xF); - -#if DV1394_DEBUG_LEVEL >= 2 - { - /* check if DMA is really running */ - int i = 0; - while (i < 20) { - mb(); - mdelay(1); - if (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) { - printk("DMA ACTIVE after %d msec\n", i); - break; - } - i++; - } - - printk("set = %08x, cmdPtr = %08x\n", - reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), - reg_read(video->ohci, video->ohci_IsoXmitCommandPtr) - ); - - if ( ! (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) { - printk("DMA did NOT go active after 20ms, event = %x\n", - reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & 0x1F); - } else - printk("DMA is RUNNING!\n"); - } -#endif - - } - - - spin_unlock_irqrestore(&video->spinlock, irq_flags); -} - - - -/*** RECEIVE FUNCTIONS *****************************************************/ - -/* - frame method put_packet - - map and copy the packet data to its location in the frame - based upon DIF section and sequence -*/ - -static void inline -frame_put_packet (struct frame *f, struct packet *p) -{ - int section_type = p->data[0] >> 5; /* section type is in bits 5 - 7 */ - int dif_sequence = p->data[1] >> 4; /* dif sequence number is in bits 4 - 7 */ - int dif_block = p->data[2]; - - /* sanity check */ - if (dif_sequence > 11 || dif_block > 149) return; - - switch (section_type) { - case 0: /* 1 Header block */ - memcpy( (void *) f->data + dif_sequence * 150 * 80, p->data, 480); - break; - - case 1: /* 2 Subcode blocks */ - memcpy( (void *) f->data + dif_sequence * 150 * 80 + (1 + dif_block) * 80, p->data, 480); - break; - - case 2: /* 3 VAUX blocks */ - memcpy( (void *) f->data + dif_sequence * 150 * 80 + (3 + dif_block) * 80, p->data, 480); - break; - - case 3: /* 9 Audio blocks interleaved with video */ - memcpy( (void *) f->data + dif_sequence * 150 * 80 + (6 + dif_block * 16) * 80, p->data, 480); - break; - - case 4: /* 135 Video blocks interleaved with audio */ - memcpy( (void *) f->data + dif_sequence * 150 * 80 + (7 + (dif_block / 15) + dif_block) * 80, p->data, 480); - break; - - default: /* we can not handle any other data */ - break; - } -} - - -static void start_dma_receive(struct video_card *video) -{ - if (video->first_run == 1) { - video->first_run = 0; - - /* start DMA once all of the frames are READY */ - video->n_clear_frames = 0; - video->first_clear_frame = -1; - video->current_packet = 0; - video->active_frame = 0; - - /* reset iso recv control register */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, 0xFFFFFFFF); - wmb(); - - /* clear bufferFill, set isochHeader and speed (0=100) */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x40000000); - - /* match on all tags, listen on channel */ - reg_write(video->ohci, video->ohci_IsoRcvContextMatch, 0xf0000000 | video->channel); - - /* address and first descriptor block + Z=1 */ - reg_write(video->ohci, video->ohci_IsoRcvCommandPtr, - video->frames[0]->descriptor_pool_dma | 1); /* Z=1 */ - wmb(); - - video->dma_running = 1; - - /* run */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x8000); - flush_pci_write(video->ohci); - - debug_printk("dv1394: DMA started\n"); - -#if DV1394_DEBUG_LEVEL >= 2 - { - int i; - - for (i = 0; i < 1000; ++i) { - mdelay(1); - if (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) { - printk("DMA ACTIVE after %d msec\n", i); - break; - } - } - if ( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) { - printk("DEAD, event = %x\n", - reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F); - } else - printk("RUNNING!\n"); - } -#endif - } else if ( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) { - debug_printk("DEAD, event = %x\n", - reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F); - - /* wake */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12)); - } -} - - -/* - receive_packets() - build the DMA program for receiving -*/ - -static void receive_packets(struct video_card *video) -{ - struct DMA_descriptor_block *block = NULL; - dma_addr_t block_dma = 0; - struct packet *data = NULL; - dma_addr_t data_dma = 0; - __le32 *last_branch_address = NULL; - unsigned long irq_flags; - int want_interrupt = 0; - struct frame *f = NULL; - int i, j; - - spin_lock_irqsave(&video->spinlock, irq_flags); - - for (j = 0; j < video->n_frames; j++) { - - /* connect frames */ - if (j > 0 && f != NULL && f->frame_end_branch != NULL) - *(f->frame_end_branch) = cpu_to_le32(video->frames[j]->descriptor_pool_dma | 1); /* set Z=1 */ - - f = video->frames[j]; - - for (i = 0; i < MAX_PACKETS; i++) { - /* locate a descriptor block and packet from the buffer */ - block = &(f->descriptor_pool[i]); - block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma; - - data = ((struct packet*)video->packet_buf.kvirt) + f->frame_num * MAX_PACKETS + i; - data_dma = dma_region_offset_to_bus( &video->packet_buf, - ((unsigned long) data - (unsigned long) video->packet_buf.kvirt) ); - - /* setup DMA descriptor block */ - want_interrupt = ((i % (MAX_PACKETS/2)) == 0 || i == (MAX_PACKETS-1)); - fill_input_last( &(block->u.in.il), want_interrupt, 512, data_dma); - - /* link descriptors */ - last_branch_address = f->frame_end_branch; - - if (last_branch_address != NULL) - *(last_branch_address) = cpu_to_le32(block_dma | 1); /* set Z=1 */ - - f->frame_end_branch = &(block->u.in.il.q[2]); - } - - } /* next j */ - - spin_unlock_irqrestore(&video->spinlock, irq_flags); - -} - - - -/*** MANAGEMENT FUNCTIONS **************************************************/ - -static int do_dv1394_init(struct video_card *video, struct dv1394_init *init) -{ - unsigned long flags, new_buf_size; - int i; - u64 chan_mask; - int retval = -EINVAL; - - debug_printk("dv1394: initialising %d\n", video->id); - if (init->api_version != DV1394_API_VERSION) - return -EINVAL; - - /* first sanitize all the parameters */ - if ( (init->n_frames < 2) || (init->n_frames > DV1394_MAX_FRAMES) ) - return -EINVAL; - - if ( (init->format != DV1394_NTSC) && (init->format != DV1394_PAL) ) - return -EINVAL; - - if ( (init->syt_offset == 0) || (init->syt_offset > 50) ) - /* default SYT offset is 3 cycles */ - init->syt_offset = 3; - - if (init->channel > 63) - init->channel = 63; - - chan_mask = (u64)1 << init->channel; - - /* calculate what size DMA buffer is needed */ - if (init->format == DV1394_NTSC) - new_buf_size = DV1394_NTSC_FRAME_SIZE * init->n_frames; - else - new_buf_size = DV1394_PAL_FRAME_SIZE * init->n_frames; - - /* round up to PAGE_SIZE */ - if (new_buf_size % PAGE_SIZE) new_buf_size += PAGE_SIZE - (new_buf_size % PAGE_SIZE); - - /* don't allow the user to allocate the DMA buffer more than once */ - if (video->dv_buf.kvirt && video->dv_buf_size != new_buf_size) { - printk("dv1394: re-sizing the DMA buffer is not allowed\n"); - return -EINVAL; - } - - /* shutdown the card if it's currently active */ - /* (the card should not be reset if the parameters are screwy) */ - - do_dv1394_shutdown(video, 0); - - /* try to claim the ISO channel */ - spin_lock_irqsave(&video->ohci->IR_channel_lock, flags); - if (video->ohci->ISO_channel_usage & chan_mask) { - spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags); - retval = -EBUSY; - goto err; - } - video->ohci->ISO_channel_usage |= chan_mask; - spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags); - - video->channel = init->channel; - - /* initialize misc. fields of video */ - video->n_frames = init->n_frames; - video->pal_or_ntsc = init->format; - - video->cip_accum = 0; - video->continuity_counter = 0; - - video->active_frame = -1; - video->first_clear_frame = 0; - video->n_clear_frames = video->n_frames; - video->dropped_frames = 0; - - video->write_off = 0; - - video->first_run = 1; - video->current_packet = -1; - video->first_frame = 0; - - if (video->pal_or_ntsc == DV1394_NTSC) { - video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_NTSC; - video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_NTSC; - video->frame_size = DV1394_NTSC_FRAME_SIZE; - } else { - video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_PAL; - video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_PAL; - video->frame_size = DV1394_PAL_FRAME_SIZE; - } - - video->syt_offset = init->syt_offset; - - /* find and claim DMA contexts on the OHCI card */ - - if (video->ohci_it_ctx == -1) { - ohci1394_init_iso_tasklet(&video->it_tasklet, OHCI_ISO_TRANSMIT, - it_tasklet_func, (unsigned long) video); - - if (ohci1394_register_iso_tasklet(video->ohci, &video->it_tasklet) < 0) { - printk(KERN_ERR "dv1394: could not find an available IT DMA context\n"); - retval = -EBUSY; - goto err; - } - - video->ohci_it_ctx = video->it_tasklet.context; - debug_printk("dv1394: claimed IT DMA context %d\n", video->ohci_it_ctx); - } - - if (video->ohci_ir_ctx == -1) { - ohci1394_init_iso_tasklet(&video->ir_tasklet, OHCI_ISO_RECEIVE, - ir_tasklet_func, (unsigned long) video); - - if (ohci1394_register_iso_tasklet(video->ohci, &video->ir_tasklet) < 0) { - printk(KERN_ERR "dv1394: could not find an available IR DMA context\n"); - retval = -EBUSY; - goto err; - } - video->ohci_ir_ctx = video->ir_tasklet.context; - debug_printk("dv1394: claimed IR DMA context %d\n", video->ohci_ir_ctx); - } - - /* allocate struct frames */ - for (i = 0; i < init->n_frames; i++) { - video->frames[i] = frame_new(i, video); - - if (!video->frames[i]) { - printk(KERN_ERR "dv1394: Cannot allocate frame structs\n"); - retval = -ENOMEM; - goto err; - } - } - - if (!video->dv_buf.kvirt) { - /* allocate the ringbuffer */ - retval = dma_region_alloc(&video->dv_buf, new_buf_size, video->ohci->dev, PCI_DMA_TODEVICE); - if (retval) - goto err; - - video->dv_buf_size = new_buf_size; - - debug_printk("dv1394: Allocated %d frame buffers, total %u pages (%u DMA pages), %lu bytes\n", - video->n_frames, video->dv_buf.n_pages, - video->dv_buf.n_dma_pages, video->dv_buf_size); - } - - /* set up the frame->data pointers */ - for (i = 0; i < video->n_frames; i++) - video->frames[i]->data = (unsigned long) video->dv_buf.kvirt + i * video->frame_size; - - if (!video->packet_buf.kvirt) { - /* allocate packet buffer */ - video->packet_buf_size = sizeof(struct packet) * video->n_frames * MAX_PACKETS; - if (video->packet_buf_size % PAGE_SIZE) - video->packet_buf_size += PAGE_SIZE - (video->packet_buf_size % PAGE_SIZE); - - retval = dma_region_alloc(&video->packet_buf, video->packet_buf_size, - video->ohci->dev, PCI_DMA_FROMDEVICE); - if (retval) - goto err; - - debug_printk("dv1394: Allocated %d packets in buffer, total %u pages (%u DMA pages), %lu bytes\n", - video->n_frames*MAX_PACKETS, video->packet_buf.n_pages, - video->packet_buf.n_dma_pages, video->packet_buf_size); - } - - /* set up register offsets for IT context */ - /* IT DMA context registers are spaced 16 bytes apart */ - video->ohci_IsoXmitContextControlSet = OHCI1394_IsoXmitContextControlSet+16*video->ohci_it_ctx; - video->ohci_IsoXmitContextControlClear = OHCI1394_IsoXmitContextControlClear+16*video->ohci_it_ctx; - video->ohci_IsoXmitCommandPtr = OHCI1394_IsoXmitCommandPtr+16*video->ohci_it_ctx; - - /* enable interrupts for IT context */ - reg_write(video->ohci, OHCI1394_IsoXmitIntMaskSet, (1 << video->ohci_it_ctx)); - debug_printk("dv1394: interrupts enabled for IT context %d\n", video->ohci_it_ctx); - - /* set up register offsets for IR context */ - /* IR DMA context registers are spaced 32 bytes apart */ - video->ohci_IsoRcvContextControlSet = OHCI1394_IsoRcvContextControlSet+32*video->ohci_ir_ctx; - video->ohci_IsoRcvContextControlClear = OHCI1394_IsoRcvContextControlClear+32*video->ohci_ir_ctx; - video->ohci_IsoRcvCommandPtr = OHCI1394_IsoRcvCommandPtr+32*video->ohci_ir_ctx; - video->ohci_IsoRcvContextMatch = OHCI1394_IsoRcvContextMatch+32*video->ohci_ir_ctx; - - /* enable interrupts for IR context */ - reg_write(video->ohci, OHCI1394_IsoRecvIntMaskSet, (1 << video->ohci_ir_ctx) ); - debug_printk("dv1394: interrupts enabled for IR context %d\n", video->ohci_ir_ctx); - - return 0; - -err: - do_dv1394_shutdown(video, 1); - return retval; -} - -/* if the user doesn't bother to call ioctl(INIT) before starting - mmap() or read()/write(), just give him some default values */ - -static int do_dv1394_init_default(struct video_card *video) -{ - struct dv1394_init init; - - init.api_version = DV1394_API_VERSION; - init.n_frames = DV1394_MAX_FRAMES / 4; - init.channel = video->channel; - init.format = video->pal_or_ntsc; - init.cip_n = video->cip_n; - init.cip_d = video->cip_d; - init.syt_offset = video->syt_offset; - - return do_dv1394_init(video, &init); -} - -/* do NOT call from interrupt context */ -static void stop_dma(struct video_card *video) -{ - unsigned long flags; - int i; - - /* no interrupts */ - spin_lock_irqsave(&video->spinlock, flags); - - video->dma_running = 0; - - if ( (video->ohci_it_ctx == -1) && (video->ohci_ir_ctx == -1) ) - goto out; - - /* stop DMA if in progress */ - if ( (video->active_frame != -1) || - (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) || - (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) { - - /* clear the .run bits */ - reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, (1 << 15)); - reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, (1 << 15)); - flush_pci_write(video->ohci); - - video->active_frame = -1; - video->first_run = 1; - - /* wait until DMA really stops */ - i = 0; - while (i < 1000) { - - /* wait 0.1 millisecond */ - udelay(100); - - if ( (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) || - (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) { - /* still active */ - debug_printk("dv1394: stop_dma: DMA not stopped yet\n" ); - mb(); - } else { - debug_printk("dv1394: stop_dma: DMA stopped safely after %d ms\n", i/10); - break; - } - - i++; - } - - if (i == 1000) { - printk(KERN_ERR "dv1394: stop_dma: DMA still going after %d ms!\n", i/10); - } - } - else - debug_printk("dv1394: stop_dma: already stopped.\n"); - -out: - spin_unlock_irqrestore(&video->spinlock, flags); -} - - - -static void do_dv1394_shutdown(struct video_card *video, int free_dv_buf) -{ - int i; - - debug_printk("dv1394: shutdown...\n"); - - /* stop DMA if in progress */ - stop_dma(video); - - /* release the DMA contexts */ - if (video->ohci_it_ctx != -1) { - video->ohci_IsoXmitContextControlSet = 0; - video->ohci_IsoXmitContextControlClear = 0; - video->ohci_IsoXmitCommandPtr = 0; - - /* disable interrupts for IT context */ - reg_write(video->ohci, OHCI1394_IsoXmitIntMaskClear, (1 << video->ohci_it_ctx)); - - /* remove tasklet */ - ohci1394_unregister_iso_tasklet(video->ohci, &video->it_tasklet); - debug_printk("dv1394: IT context %d released\n", video->ohci_it_ctx); - video->ohci_it_ctx = -1; - } - - if (video->ohci_ir_ctx != -1) { - video->ohci_IsoRcvContextControlSet = 0; - video->ohci_IsoRcvContextControlClear = 0; - video->ohci_IsoRcvCommandPtr = 0; - video->ohci_IsoRcvContextMatch = 0; - - /* disable interrupts for IR context */ - reg_write(video->ohci, OHCI1394_IsoRecvIntMaskClear, (1 << video->ohci_ir_ctx)); - - /* remove tasklet */ - ohci1394_unregister_iso_tasklet(video->ohci, &video->ir_tasklet); - debug_printk("dv1394: IR context %d released\n", video->ohci_ir_ctx); - video->ohci_ir_ctx = -1; - } - - /* release the ISO channel */ - if (video->channel != -1) { - u64 chan_mask; - unsigned long flags; - - chan_mask = (u64)1 << video->channel; - - spin_lock_irqsave(&video->ohci->IR_channel_lock, flags); - video->ohci->ISO_channel_usage &= ~(chan_mask); - spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags); - - video->channel = -1; - } - - /* free the frame structs */ - for (i = 0; i < DV1394_MAX_FRAMES; i++) { - if (video->frames[i]) - frame_delete(video->frames[i]); - video->frames[i] = NULL; - } - - video->n_frames = 0; - - /* we can't free the DMA buffer unless it is guaranteed that - no more user-space mappings exist */ - - if (free_dv_buf) { - dma_region_free(&video->dv_buf); - video->dv_buf_size = 0; - } - - /* free packet buffer */ - dma_region_free(&video->packet_buf); - video->packet_buf_size = 0; - - debug_printk("dv1394: shutdown OK\n"); -} - -/* - ********************************** - *** MMAP() THEORY OF OPERATION *** - ********************************** - - The ringbuffer cannot be re-allocated or freed while - a user program maintains a mapping of it. (note that a mapping - can persist even after the device fd is closed!) - - So, only let the user process allocate the DMA buffer once. - To resize or deallocate it, you must close the device file - and open it again. - - Previously Dan M. hacked out a scheme that allowed the DMA - buffer to change by forcefully unmapping it from the user's - address space. It was prone to error because it's very hard to - track all the places the buffer could have been mapped (we - would have had to walk the vma list of every process in the - system to be sure we found all the mappings!). Instead, we - force the user to choose one buffer size and stick with - it. This small sacrifice is worth the huge reduction in - error-prone code in dv1394. -*/ - -static int dv1394_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_card *video = file_to_video_card(file); - int retval = -EINVAL; - - /* - * We cannot use the blocking variant mutex_lock here because .mmap - * is called with mmap_sem held, while .ioctl, .read, .write acquire - * video->mtx and subsequently call copy_to/from_user which will - * grab mmap_sem in case of a page fault. - */ - if (!mutex_trylock(&video->mtx)) - return -EAGAIN; - - if ( ! video_card_initialized(video) ) { - retval = do_dv1394_init_default(video); - if (retval) - goto out; - } - - retval = dma_region_mmap(&video->dv_buf, file, vma); -out: - mutex_unlock(&video->mtx); - return retval; -} - -/*** DEVICE FILE INTERFACE *************************************************/ - -/* no need to serialize, multiple threads OK */ -static unsigned int dv1394_poll(struct file *file, struct poll_table_struct *wait) -{ - struct video_card *video = file_to_video_card(file); - unsigned int mask = 0; - unsigned long flags; - - poll_wait(file, &video->waitq, wait); - - spin_lock_irqsave(&video->spinlock, flags); - if ( video->n_frames == 0 ) { - - } else if ( video->active_frame == -1 ) { - /* nothing going on */ - mask |= POLLOUT; - } else { - /* any clear/ready buffers? */ - if (video->n_clear_frames >0) - mask |= POLLOUT | POLLIN; - } - spin_unlock_irqrestore(&video->spinlock, flags); - - return mask; -} - -static int dv1394_fasync(int fd, struct file *file, int on) -{ - /* I just copied this code verbatim from Alan Cox's mouse driver example - (Documentation/DocBook/) */ - - struct video_card *video = file_to_video_card(file); - - return fasync_helper(fd, file, on, &video->fasync); -} - -static ssize_t dv1394_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct video_card *video = file_to_video_card(file); - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - size_t cnt; - unsigned long flags; - int target_frame; - - /* serialize this to prevent multi-threaded mayhem */ - if (file->f_flags & O_NONBLOCK) { - if (!mutex_trylock(&video->mtx)) - return -EAGAIN; - } else { - if (mutex_lock_interruptible(&video->mtx)) - return -ERESTARTSYS; - } - - if ( !video_card_initialized(video) ) { - ret = do_dv1394_init_default(video); - if (ret) { - mutex_unlock(&video->mtx); - return ret; - } - } - - ret = 0; - add_wait_queue(&video->waitq, &wait); - - while (count > 0) { - - /* must set TASK_INTERRUPTIBLE *before* checking for free - buffers; otherwise we could miss a wakeup if the interrupt - fires between the check and the schedule() */ - - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irqsave(&video->spinlock, flags); - - target_frame = video->first_clear_frame; - - spin_unlock_irqrestore(&video->spinlock, flags); - - if (video->frames[target_frame]->state == FRAME_CLEAR) { - - /* how much room is left in the target frame buffer */ - cnt = video->frame_size - (video->write_off - target_frame * video->frame_size); - - } else { - /* buffer is already used */ - cnt = 0; - } - - if (cnt > count) - cnt = count; - - if (cnt <= 0) { - /* no room left, gotta wait */ - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - - schedule(); - - continue; /* start over from 'while(count > 0)...' */ - } - - if (copy_from_user(video->dv_buf.kvirt + video->write_off, buffer, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - - video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size); - - count -= cnt; - buffer += cnt; - ret += cnt; - - if (video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames)) - frame_prepare(video, target_frame); - } - - remove_wait_queue(&video->waitq, &wait); - set_current_state(TASK_RUNNING); - mutex_unlock(&video->mtx); - return ret; -} - - -static ssize_t dv1394_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct video_card *video = file_to_video_card(file); - DECLARE_WAITQUEUE(wait, current); - ssize_t ret; - size_t cnt; - unsigned long flags; - int target_frame; - - /* serialize this to prevent multi-threaded mayhem */ - if (file->f_flags & O_NONBLOCK) { - if (!mutex_trylock(&video->mtx)) - return -EAGAIN; - } else { - if (mutex_lock_interruptible(&video->mtx)) - return -ERESTARTSYS; - } - - if ( !video_card_initialized(video) ) { - ret = do_dv1394_init_default(video); - if (ret) { - mutex_unlock(&video->mtx); - return ret; - } - video->continuity_counter = -1; - - receive_packets(video); - - start_dma_receive(video); - } - - ret = 0; - add_wait_queue(&video->waitq, &wait); - - while (count > 0) { - - /* must set TASK_INTERRUPTIBLE *before* checking for free - buffers; otherwise we could miss a wakeup if the interrupt - fires between the check and the schedule() */ - - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irqsave(&video->spinlock, flags); - - target_frame = video->first_clear_frame; - - spin_unlock_irqrestore(&video->spinlock, flags); - - if (target_frame >= 0 && - video->n_clear_frames > 0 && - video->frames[target_frame]->state == FRAME_CLEAR) { - - /* how much room is left in the target frame buffer */ - cnt = video->frame_size - (video->write_off - target_frame * video->frame_size); - - } else { - /* buffer is already used */ - cnt = 0; - } - - if (cnt > count) - cnt = count; - - if (cnt <= 0) { - /* no room left, gotta wait */ - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - - schedule(); - - continue; /* start over from 'while(count > 0)...' */ - } - - if (copy_to_user(buffer, video->dv_buf.kvirt + video->write_off, cnt)) { - if (!ret) - ret = -EFAULT; - break; - } - - video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size); - - count -= cnt; - buffer += cnt; - ret += cnt; - - if (video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames)) { - spin_lock_irqsave(&video->spinlock, flags); - video->n_clear_frames--; - video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames; - spin_unlock_irqrestore(&video->spinlock, flags); - } - } - - remove_wait_queue(&video->waitq, &wait); - set_current_state(TASK_RUNNING); - mutex_unlock(&video->mtx); - return ret; -} - - -/*** DEVICE IOCTL INTERFACE ************************************************/ - -static long dv1394_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct video_card *video = file_to_video_card(file); - unsigned long flags; - int ret = -EINVAL; - void __user *argp = (void __user *)arg; - - DECLARE_WAITQUEUE(wait, current); - - /* serialize this to prevent multi-threaded mayhem */ - if (file->f_flags & O_NONBLOCK) { - if (!mutex_trylock(&video->mtx)) - return -EAGAIN; - } else { - if (mutex_lock_interruptible(&video->mtx)) - return -ERESTARTSYS; - } - - switch(cmd) - { - case DV1394_IOC_SUBMIT_FRAMES: { - unsigned int n_submit; - - if ( !video_card_initialized(video) ) { - ret = do_dv1394_init_default(video); - if (ret) - goto out; - } - - n_submit = (unsigned int) arg; - - if (n_submit > video->n_frames) { - ret = -EINVAL; - goto out; - } - - while (n_submit > 0) { - - add_wait_queue(&video->waitq, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irqsave(&video->spinlock, flags); - - /* wait until video->first_clear_frame is really CLEAR */ - while (video->frames[video->first_clear_frame]->state != FRAME_CLEAR) { - - spin_unlock_irqrestore(&video->spinlock, flags); - - if (signal_pending(current)) { - remove_wait_queue(&video->waitq, &wait); - set_current_state(TASK_RUNNING); - ret = -EINTR; - goto out; - } - - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irqsave(&video->spinlock, flags); - } - spin_unlock_irqrestore(&video->spinlock, flags); - - remove_wait_queue(&video->waitq, &wait); - set_current_state(TASK_RUNNING); - - frame_prepare(video, video->first_clear_frame); - - n_submit--; - } - - ret = 0; - break; - } - - case DV1394_IOC_WAIT_FRAMES: { - unsigned int n_wait; - - if ( !video_card_initialized(video) ) { - ret = -EINVAL; - goto out; - } - - n_wait = (unsigned int) arg; - - /* since we re-run the last frame on underflow, we will - never actually have n_frames clear frames; at most only - n_frames - 1 */ - - if (n_wait > (video->n_frames-1) ) { - ret = -EINVAL; - goto out; - } - - add_wait_queue(&video->waitq, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irqsave(&video->spinlock, flags); - - while (video->n_clear_frames < n_wait) { - - spin_unlock_irqrestore(&video->spinlock, flags); - - if (signal_pending(current)) { - remove_wait_queue(&video->waitq, &wait); - set_current_state(TASK_RUNNING); - ret = -EINTR; - goto out; - } - - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irqsave(&video->spinlock, flags); - } - - spin_unlock_irqrestore(&video->spinlock, flags); - - remove_wait_queue(&video->waitq, &wait); - set_current_state(TASK_RUNNING); - ret = 0; - break; - } - - case DV1394_IOC_RECEIVE_FRAMES: { - unsigned int n_recv; - - if ( !video_card_initialized(video) ) { - ret = -EINVAL; - goto out; - } - - n_recv = (unsigned int) arg; - - /* at least one frame must be active */ - if (n_recv > (video->n_frames-1) ) { - ret = -EINVAL; - goto out; - } - - spin_lock_irqsave(&video->spinlock, flags); - - /* release the clear frames */ - video->n_clear_frames -= n_recv; - - /* advance the clear frame cursor */ - video->first_clear_frame = (video->first_clear_frame + n_recv) % video->n_frames; - - /* reset dropped_frames */ - video->dropped_frames = 0; - - spin_unlock_irqrestore(&video->spinlock, flags); - - ret = 0; - break; - } - - case DV1394_IOC_START_RECEIVE: { - if ( !video_card_initialized(video) ) { - ret = do_dv1394_init_default(video); - if (ret) - goto out; - } - - video->continuity_counter = -1; - - receive_packets(video); - - start_dma_receive(video); - - ret = 0; - break; - } - - case DV1394_IOC_INIT: { - struct dv1394_init init; - if (!argp) { - ret = do_dv1394_init_default(video); - } else { - if (copy_from_user(&init, argp, sizeof(init))) { - ret = -EFAULT; - goto out; - } - ret = do_dv1394_init(video, &init); - } - break; - } - - case DV1394_IOC_SHUTDOWN: - do_dv1394_shutdown(video, 0); - ret = 0; - break; - - - case DV1394_IOC_GET_STATUS: { - struct dv1394_status status; - - if ( !video_card_initialized(video) ) { - ret = -EINVAL; - goto out; - } - - status.init.api_version = DV1394_API_VERSION; - status.init.channel = video->channel; - status.init.n_frames = video->n_frames; - status.init.format = video->pal_or_ntsc; - status.init.cip_n = video->cip_n; - status.init.cip_d = video->cip_d; - status.init.syt_offset = video->syt_offset; - - status.first_clear_frame = video->first_clear_frame; - - /* the rest of the fields need to be locked against the interrupt */ - spin_lock_irqsave(&video->spinlock, flags); - - status.active_frame = video->active_frame; - status.n_clear_frames = video->n_clear_frames; - - status.dropped_frames = video->dropped_frames; - - /* reset dropped_frames */ - video->dropped_frames = 0; - - spin_unlock_irqrestore(&video->spinlock, flags); - - if (copy_to_user(argp, &status, sizeof(status))) { - ret = -EFAULT; - goto out; - } - - ret = 0; - break; - } - - default: - break; - } - - out: - mutex_unlock(&video->mtx); - return ret; -} - -/*** DEVICE FILE INTERFACE CONTINUED ***************************************/ - -static int dv1394_open(struct inode *inode, struct file *file) -{ - struct video_card *video = NULL; - - if (file->private_data) { - video = file->private_data; - - } else { - /* look up the card by ID */ - unsigned long flags; - int idx = ieee1394_file_to_instance(file); - - spin_lock_irqsave(&dv1394_cards_lock, flags); - if (!list_empty(&dv1394_cards)) { - struct video_card *p; - list_for_each_entry(p, &dv1394_cards, list) { - if ((p->id) == idx) { - video = p; - break; - } - } - } - spin_unlock_irqrestore(&dv1394_cards_lock, flags); - - if (!video) { - debug_printk("dv1394: OHCI card %d not found", idx); - return -ENODEV; - } - - file->private_data = (void*) video; - } - -#ifndef DV1394_ALLOW_MORE_THAN_ONE_OPEN - - if ( test_and_set_bit(0, &video->open) ) { - /* video is already open by someone else */ - return -EBUSY; - } - -#endif - - printk(KERN_INFO "%s: NOTE, the dv1394 interface is unsupported " - "and will not be available in the new firewire driver stack. " - "Try libraw1394 based programs instead.\n", current->comm); - - return nonseekable_open(inode, file); -} - - -static int dv1394_release(struct inode *inode, struct file *file) -{ - struct video_card *video = file_to_video_card(file); - - /* OK to free the DMA buffer, no more mappings can exist */ - do_dv1394_shutdown(video, 1); - - /* give someone else a turn */ - clear_bit(0, &video->open); - - return 0; -} - - -/*** DEVICE DRIVER HANDLERS ************************************************/ - -static void it_tasklet_func(unsigned long data) -{ - int wake = 0; - struct video_card *video = (struct video_card*) data; - - spin_lock(&video->spinlock); - - if (!video->dma_running) - goto out; - - irq_printk("ContextControl = %08x, CommandPtr = %08x\n", - reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), - reg_read(video->ohci, video->ohci_IsoXmitCommandPtr) - ); - - - if ( (video->ohci_it_ctx != -1) && - (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) { - - struct frame *f; - unsigned int frame, i; - - - if (video->active_frame == -1) - frame = 0; - else - frame = video->active_frame; - - /* check all the DMA-able frames */ - for (i = 0; i < video->n_frames; i++, frame = (frame+1) % video->n_frames) { - - irq_printk("IRQ checking frame %d...", frame); - f = video->frames[frame]; - if (f->state != FRAME_READY) { - irq_printk("clear, skipping\n"); - /* we don't own this frame */ - continue; - } - - irq_printk("DMA\n"); - - /* check the frame begin semaphore to see if we can free the previous frame */ - if ( *(f->frame_begin_timestamp) ) { - int prev_frame; - struct frame *prev_f; - - - - /* don't reset, need this later *(f->frame_begin_timestamp) = 0; */ - irq_printk(" BEGIN\n"); - - prev_frame = frame - 1; - if (prev_frame == -1) - prev_frame += video->n_frames; - prev_f = video->frames[prev_frame]; - - /* make sure we can actually garbage collect - this frame */ - if ( (prev_f->state == FRAME_READY) && - prev_f->done && (!f->done) ) - { - frame_reset(prev_f); - video->n_clear_frames++; - wake = 1; - video->active_frame = frame; - - irq_printk(" BEGIN - freeing previous frame %d, new active frame is %d\n", prev_frame, frame); - } else { - irq_printk(" BEGIN - can't free yet\n"); - } - - f->done = 1; - } - - - /* see if we need to set the timestamp for the next frame */ - if ( *(f->mid_frame_timestamp) ) { - struct frame *next_frame; - u32 begin_ts, ts_cyc, ts_off; - - *(f->mid_frame_timestamp) = 0; - - begin_ts = le32_to_cpu(*(f->frame_begin_timestamp)); - - irq_printk(" MIDDLE - first packet was sent at cycle %4u (%2u), assigned timestamp was (%2u) %4u\n", - begin_ts & 0x1FFF, begin_ts & 0xF, - f->assigned_timestamp >> 12, f->assigned_timestamp & 0xFFF); - - /* prepare next frame and assign timestamp */ - next_frame = video->frames[ (frame+1) % video->n_frames ]; - - if (next_frame->state == FRAME_READY) { - irq_printk(" MIDDLE - next frame is ready, good\n"); - } else { - debug_printk("dv1394: Underflow! At least one frame has been dropped.\n"); - next_frame = f; - } - - /* set the timestamp to the timestamp of the last frame sent, - plus the length of the last frame sent, plus the syt latency */ - ts_cyc = begin_ts & 0xF; - /* advance one frame, plus syt latency (typically 2-3) */ - ts_cyc += f->n_packets + video->syt_offset ; - - ts_off = 0; - - ts_cyc += ts_off/3072; - ts_off %= 3072; - - next_frame->assigned_timestamp = ((ts_cyc&0xF) << 12) + ts_off; - if (next_frame->cip_syt1) { - next_frame->cip_syt1->b[6] = next_frame->assigned_timestamp >> 8; - next_frame->cip_syt1->b[7] = next_frame->assigned_timestamp & 0xFF; - } - if (next_frame->cip_syt2) { - next_frame->cip_syt2->b[6] = next_frame->assigned_timestamp >> 8; - next_frame->cip_syt2->b[7] = next_frame->assigned_timestamp & 0xFF; - } - - } - - /* see if the frame looped */ - if ( *(f->frame_end_timestamp) ) { - - *(f->frame_end_timestamp) = 0; - - debug_printk(" END - the frame looped at least once\n"); - - video->dropped_frames++; - } - - } /* for (each frame) */ - } - - if (wake) { - kill_fasync(&video->fasync, SIGIO, POLL_OUT); - - /* wake readers/writers/ioctl'ers */ - wake_up_interruptible(&video->waitq); - } - -out: - spin_unlock(&video->spinlock); -} - -static void ir_tasklet_func(unsigned long data) -{ - int wake = 0; - struct video_card *video = (struct video_card*) data; - - spin_lock(&video->spinlock); - - if (!video->dma_running) - goto out; - - if ( (video->ohci_ir_ctx != -1) && - (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) ) { - - int sof=0; /* start-of-frame flag */ - struct frame *f; - u16 packet_length; - int i, dbc=0; - struct DMA_descriptor_block *block = NULL; - u16 xferstatus; - - int next_i, prev_i; - struct DMA_descriptor_block *next = NULL; - dma_addr_t next_dma = 0; - struct DMA_descriptor_block *prev = NULL; - - /* loop over all descriptors in all frames */ - for (i = 0; i < video->n_frames*MAX_PACKETS; i++) { - struct packet *p = dma_region_i(&video->packet_buf, struct packet, video->current_packet); - - /* make sure we are seeing the latest changes to p */ - dma_region_sync_for_cpu(&video->packet_buf, - (unsigned long) p - (unsigned long) video->packet_buf.kvirt, - sizeof(struct packet)); - - packet_length = le16_to_cpu(p->data_length); - - /* get the descriptor based on packet_buffer cursor */ - f = video->frames[video->current_packet / MAX_PACKETS]; - block = &(f->descriptor_pool[video->current_packet % MAX_PACKETS]); - xferstatus = le32_to_cpu(block->u.in.il.q[3]) >> 16; - xferstatus &= 0x1F; - irq_printk("ir_tasklet_func: xferStatus/resCount [%d] = 0x%08x\n", i, le32_to_cpu(block->u.in.il.q[3]) ); - - /* get the current frame */ - f = video->frames[video->active_frame]; - - /* exclude empty packet */ - if (packet_length > 8 && xferstatus == 0x11) { - /* check for start of frame */ - /* DRD> Changed to check section type ([0]>>5==0) - and dif sequence ([1]>>4==0) */ - sof = ( (p->data[0] >> 5) == 0 && (p->data[1] >> 4) == 0); - - dbc = (int) (p->cip_h1 >> 24); - if ( video->continuity_counter != -1 && dbc > ((video->continuity_counter + 1) % 256) ) - { - printk(KERN_WARNING "dv1394: discontinuity detected, dropping all frames\n" ); - video->dropped_frames += video->n_clear_frames + 1; - video->first_frame = 0; - video->n_clear_frames = 0; - video->first_clear_frame = -1; - } - video->continuity_counter = dbc; - - if (!video->first_frame) { - if (sof) { - video->first_frame = 1; - } - - } else if (sof) { - /* close current frame */ - frame_reset(f); /* f->state = STATE_CLEAR */ - video->n_clear_frames++; - if (video->n_clear_frames > video->n_frames) { - video->dropped_frames++; - printk(KERN_WARNING "dv1394: dropped a frame during reception\n" ); - video->n_clear_frames = video->n_frames-1; - video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames; - } - if (video->first_clear_frame == -1) - video->first_clear_frame = video->active_frame; - - /* get the next frame */ - video->active_frame = (video->active_frame + 1) % video->n_frames; - f = video->frames[video->active_frame]; - irq_printk(" frame received, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n", - video->active_frame, video->n_clear_frames, video->first_clear_frame); - } - if (video->first_frame) { - if (sof) { - /* open next frame */ - f->state = FRAME_READY; - } - - /* copy to buffer */ - if (f->n_packets > (video->frame_size / 480)) { - printk(KERN_ERR "frame buffer overflow during receive\n"); - } - - frame_put_packet(f, p); - - } /* first_frame */ - } - - /* stop, end of ready packets */ - else if (xferstatus == 0) { - break; - } - - /* reset xferStatus & resCount */ - block->u.in.il.q[3] = cpu_to_le32(512); - - /* terminate dma chain at this (next) packet */ - next_i = video->current_packet; - f = video->frames[next_i / MAX_PACKETS]; - next = &(f->descriptor_pool[next_i % MAX_PACKETS]); - next_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma; - next->u.in.il.q[0] |= cpu_to_le32(3 << 20); /* enable interrupt */ - next->u.in.il.q[2] = cpu_to_le32(0); /* disable branch */ - - /* link previous to next */ - prev_i = (next_i == 0) ? (MAX_PACKETS * video->n_frames - 1) : (next_i - 1); - f = video->frames[prev_i / MAX_PACKETS]; - prev = &(f->descriptor_pool[prev_i % MAX_PACKETS]); - if (prev_i % (MAX_PACKETS/2)) { - prev->u.in.il.q[0] &= ~cpu_to_le32(3 << 20); /* no interrupt */ - } else { - prev->u.in.il.q[0] |= cpu_to_le32(3 << 20); /* enable interrupt */ - } - prev->u.in.il.q[2] = cpu_to_le32(next_dma | 1); /* set Z=1 */ - wmb(); - - /* wake up DMA in case it fell asleep */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12)); - - /* advance packet_buffer cursor */ - video->current_packet = (video->current_packet + 1) % (MAX_PACKETS * video->n_frames); - - } /* for all packets */ - - wake = 1; /* why the hell not? */ - - } /* receive interrupt */ - - if (wake) { - kill_fasync(&video->fasync, SIGIO, POLL_IN); - - /* wake readers/writers/ioctl'ers */ - wake_up_interruptible(&video->waitq); - } - -out: - spin_unlock(&video->spinlock); -} - -static struct cdev dv1394_cdev; -static const struct file_operations dv1394_fops= -{ - .owner = THIS_MODULE, - .poll = dv1394_poll, - .unlocked_ioctl = dv1394_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = dv1394_compat_ioctl, -#endif - .mmap = dv1394_mmap, - .open = dv1394_open, - .write = dv1394_write, - .read = dv1394_read, - .release = dv1394_release, - .fasync = dv1394_fasync, - .llseek = no_llseek, -}; - - -/*** HOTPLUG STUFF **********************************************************/ -/* - * Export information about protocols/devices supported by this driver. - */ -#ifdef MODULE -static const struct ieee1394_device_id dv1394_id_table[] = { - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = AVC_SW_VERSION_ENTRY & 0xffffff - }, - { } -}; - -MODULE_DEVICE_TABLE(ieee1394, dv1394_id_table); -#endif /* MODULE */ - -static struct hpsb_protocol_driver dv1394_driver = { - .name = "dv1394", -}; - - -/*** IEEE1394 HPSB CALLBACKS ***********************************************/ - -static int dv1394_init(struct ti_ohci *ohci, enum pal_or_ntsc format, enum modes mode) -{ - struct video_card *video; - unsigned long flags; - int i; - - video = kzalloc(sizeof(*video), GFP_KERNEL); - if (!video) { - printk(KERN_ERR "dv1394: cannot allocate video_card\n"); - return -1; - } - - video->ohci = ohci; - /* lower 2 bits of id indicate which of four "plugs" - per host */ - video->id = ohci->host->id << 2; - if (format == DV1394_NTSC) - video->id |= mode; - else - video->id |= 2 + mode; - - video->ohci_it_ctx = -1; - video->ohci_ir_ctx = -1; - - video->ohci_IsoXmitContextControlSet = 0; - video->ohci_IsoXmitContextControlClear = 0; - video->ohci_IsoXmitCommandPtr = 0; - - video->ohci_IsoRcvContextControlSet = 0; - video->ohci_IsoRcvContextControlClear = 0; - video->ohci_IsoRcvCommandPtr = 0; - video->ohci_IsoRcvContextMatch = 0; - - video->n_frames = 0; /* flag that video is not initialized */ - video->channel = 63; /* default to broadcast channel */ - video->active_frame = -1; - - /* initialize the following */ - video->pal_or_ntsc = format; - video->cip_n = 0; /* 0 = use builtin default */ - video->cip_d = 0; - video->syt_offset = 0; - video->mode = mode; - - for (i = 0; i < DV1394_MAX_FRAMES; i++) - video->frames[i] = NULL; - - dma_region_init(&video->dv_buf); - video->dv_buf_size = 0; - dma_region_init(&video->packet_buf); - video->packet_buf_size = 0; - - clear_bit(0, &video->open); - spin_lock_init(&video->spinlock); - video->dma_running = 0; - mutex_init(&video->mtx); - init_waitqueue_head(&video->waitq); - video->fasync = NULL; - - spin_lock_irqsave(&dv1394_cards_lock, flags); - INIT_LIST_HEAD(&video->list); - list_add_tail(&video->list, &dv1394_cards); - spin_unlock_irqrestore(&dv1394_cards_lock, flags); - - debug_printk("dv1394: dv1394_init() OK on ID %d\n", video->id); - return 0; -} - -static void dv1394_remove_host(struct hpsb_host *host) -{ - struct video_card *video, *tmp_video; - unsigned long flags; - int found_ohci_card = 0; - - do { - video = NULL; - spin_lock_irqsave(&dv1394_cards_lock, flags); - list_for_each_entry(tmp_video, &dv1394_cards, list) { - if ((tmp_video->id >> 2) == host->id) { - list_del(&tmp_video->list); - video = tmp_video; - found_ohci_card = 1; - break; - } - } - spin_unlock_irqrestore(&dv1394_cards_lock, flags); - - if (video) { - do_dv1394_shutdown(video, 1); - kfree(video); - } - } while (video); - - if (found_ohci_card) - device_destroy(hpsb_protocol_class, MKDEV(IEEE1394_MAJOR, - IEEE1394_MINOR_BLOCK_DV1394 * 16 + (host->id << 2))); -} - -static void dv1394_add_host(struct hpsb_host *host) -{ - struct ti_ohci *ohci; - int id = host->id; - - /* We only work with the OHCI-1394 driver */ - if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) - return; - - ohci = (struct ti_ohci *)host->hostdata; - - device_create(hpsb_protocol_class, NULL, - MKDEV(IEEE1394_MAJOR, - IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id<<2)), - NULL, "dv1394-%d", id); - - dv1394_init(ohci, DV1394_NTSC, MODE_RECEIVE); - dv1394_init(ohci, DV1394_NTSC, MODE_TRANSMIT); - dv1394_init(ohci, DV1394_PAL, MODE_RECEIVE); - dv1394_init(ohci, DV1394_PAL, MODE_TRANSMIT); -} - - -/* Bus reset handler. In the event of a bus reset, we may need to - re-start the DMA contexts - otherwise the user program would - end up waiting forever. -*/ - -static void dv1394_host_reset(struct hpsb_host *host) -{ - struct video_card *video = NULL, *tmp_vid; - unsigned long flags; - - /* We only work with the OHCI-1394 driver */ - if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) - return; - - /* find the corresponding video_cards */ - spin_lock_irqsave(&dv1394_cards_lock, flags); - list_for_each_entry(tmp_vid, &dv1394_cards, list) { - if ((tmp_vid->id >> 2) == host->id) { - video = tmp_vid; - break; - } - } - spin_unlock_irqrestore(&dv1394_cards_lock, flags); - - if (!video) - return; - - - spin_lock_irqsave(&video->spinlock, flags); - - if (!video->dma_running) - goto out; - - /* check IT context */ - if (video->ohci_it_ctx != -1) { - u32 ctx; - - ctx = reg_read(video->ohci, video->ohci_IsoXmitContextControlSet); - - /* if (RUN but not ACTIVE) */ - if ( (ctx & (1<<15)) && - !(ctx & (1<<10)) ) { - - debug_printk("dv1394: IT context stopped due to bus reset; waking it up\n"); - - /* to be safe, assume a frame has been dropped. User-space programs - should handle this condition like an underflow. */ - video->dropped_frames++; - - /* for some reason you must clear, then re-set the RUN bit to restart DMA */ - - /* clear RUN */ - reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, (1 << 15)); - flush_pci_write(video->ohci); - - /* set RUN */ - reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, (1 << 15)); - flush_pci_write(video->ohci); - - /* set the WAKE bit (just in case; this isn't strictly necessary) */ - reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, (1 << 12)); - flush_pci_write(video->ohci); - - irq_printk("dv1394: AFTER IT restart ctx 0x%08x ptr 0x%08x\n", - reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), - reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)); - } - } - - /* check IR context */ - if (video->ohci_ir_ctx != -1) { - u32 ctx; - - ctx = reg_read(video->ohci, video->ohci_IsoRcvContextControlSet); - - /* if (RUN but not ACTIVE) */ - if ( (ctx & (1<<15)) && - !(ctx & (1<<10)) ) { - - debug_printk("dv1394: IR context stopped due to bus reset; waking it up\n"); - - /* to be safe, assume a frame has been dropped. User-space programs - should handle this condition like an overflow. */ - video->dropped_frames++; - - /* for some reason you must clear, then re-set the RUN bit to restart DMA */ - /* XXX this doesn't work for me, I can't get IR DMA to restart :[ */ - - /* clear RUN */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, (1 << 15)); - flush_pci_write(video->ohci); - - /* set RUN */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 15)); - flush_pci_write(video->ohci); - - /* set the WAKE bit (just in case; this isn't strictly necessary) */ - reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12)); - flush_pci_write(video->ohci); - - irq_printk("dv1394: AFTER IR restart ctx 0x%08x ptr 0x%08x\n", - reg_read(video->ohci, video->ohci_IsoRcvContextControlSet), - reg_read(video->ohci, video->ohci_IsoRcvCommandPtr)); - } - } - -out: - spin_unlock_irqrestore(&video->spinlock, flags); - - /* wake readers/writers/ioctl'ers */ - wake_up_interruptible(&video->waitq); -} - -static struct hpsb_highlevel dv1394_highlevel = { - .name = "dv1394", - .add_host = dv1394_add_host, - .remove_host = dv1394_remove_host, - .host_reset = dv1394_host_reset, -}; - -#ifdef CONFIG_COMPAT - -#define DV1394_IOC32_INIT _IOW('#', 0x06, struct dv1394_init32) -#define DV1394_IOC32_GET_STATUS _IOR('#', 0x0c, struct dv1394_status32) - -struct dv1394_init32 { - u32 api_version; - u32 channel; - u32 n_frames; - u32 format; - u32 cip_n; - u32 cip_d; - u32 syt_offset; -}; - -struct dv1394_status32 { - struct dv1394_init32 init; - s32 active_frame; - u32 first_clear_frame; - u32 n_clear_frames; - u32 dropped_frames; -}; - -/* RED-PEN: this should use compat_alloc_userspace instead */ - -static int handle_dv1394_init(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct dv1394_init32 dv32; - struct dv1394_init dv; - mm_segment_t old_fs; - int ret; - - if (file->f_op->unlocked_ioctl != dv1394_ioctl) - return -EFAULT; - - if (copy_from_user(&dv32, (void __user *)arg, sizeof(dv32))) - return -EFAULT; - - dv.api_version = dv32.api_version; - dv.channel = dv32.channel; - dv.n_frames = dv32.n_frames; - dv.format = dv32.format; - dv.cip_n = (unsigned long)dv32.cip_n; - dv.cip_d = (unsigned long)dv32.cip_d; - dv.syt_offset = dv32.syt_offset; - - old_fs = get_fs(); - set_fs(KERNEL_DS); - ret = dv1394_ioctl(file, DV1394_IOC_INIT, (unsigned long)&dv); - set_fs(old_fs); - - return ret; -} - -static int handle_dv1394_get_status(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct dv1394_status32 dv32; - struct dv1394_status dv; - mm_segment_t old_fs; - int ret; - - if (file->f_op->unlocked_ioctl != dv1394_ioctl) - return -EFAULT; - - old_fs = get_fs(); - set_fs(KERNEL_DS); - ret = dv1394_ioctl(file, DV1394_IOC_GET_STATUS, (unsigned long)&dv); - set_fs(old_fs); - - if (!ret) { - dv32.init.api_version = dv.init.api_version; - dv32.init.channel = dv.init.channel; - dv32.init.n_frames = dv.init.n_frames; - dv32.init.format = dv.init.format; - dv32.init.cip_n = (u32)dv.init.cip_n; - dv32.init.cip_d = (u32)dv.init.cip_d; - dv32.init.syt_offset = dv.init.syt_offset; - dv32.active_frame = dv.active_frame; - dv32.first_clear_frame = dv.first_clear_frame; - dv32.n_clear_frames = dv.n_clear_frames; - dv32.dropped_frames = dv.dropped_frames; - - if (copy_to_user((struct dv1394_status32 __user *)arg, &dv32, sizeof(dv32))) - ret = -EFAULT; - } - - return ret; -} - - - -static long dv1394_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - switch (cmd) { - case DV1394_IOC_SHUTDOWN: - case DV1394_IOC_SUBMIT_FRAMES: - case DV1394_IOC_WAIT_FRAMES: - case DV1394_IOC_RECEIVE_FRAMES: - case DV1394_IOC_START_RECEIVE: - return dv1394_ioctl(file, cmd, arg); - - case DV1394_IOC32_INIT: - return handle_dv1394_init(file, cmd, arg); - case DV1394_IOC32_GET_STATUS: - return handle_dv1394_get_status(file, cmd, arg); - default: - return -ENOIOCTLCMD; - } -} - -#endif /* CONFIG_COMPAT */ - - -/*** KERNEL MODULE HANDLERS ************************************************/ - -MODULE_AUTHOR("Dan Maas , Dan Dennedy "); -MODULE_DESCRIPTION("driver for DV input/output on OHCI board"); -MODULE_SUPPORTED_DEVICE("dv1394"); -MODULE_LICENSE("GPL"); - -static void __exit dv1394_exit_module(void) -{ - hpsb_unregister_protocol(&dv1394_driver); - hpsb_unregister_highlevel(&dv1394_highlevel); - cdev_del(&dv1394_cdev); -} - -static int __init dv1394_init_module(void) -{ - int ret; - - cdev_init(&dv1394_cdev, &dv1394_fops); - dv1394_cdev.owner = THIS_MODULE; - ret = cdev_add(&dv1394_cdev, IEEE1394_DV1394_DEV, 16); - if (ret) { - printk(KERN_ERR "dv1394: unable to register character device\n"); - return ret; - } - - hpsb_register_highlevel(&dv1394_highlevel); - - ret = hpsb_register_protocol(&dv1394_driver); - if (ret) { - printk(KERN_ERR "dv1394: failed to register protocol\n"); - hpsb_unregister_highlevel(&dv1394_highlevel); - cdev_del(&dv1394_cdev); - return ret; - } - - return 0; -} - -module_init(dv1394_init_module); -module_exit(dv1394_exit_module); diff --git a/drivers/ieee1394/dv1394.h b/drivers/ieee1394/dv1394.h deleted file mode 100644 index 5807f5289810..000000000000 --- a/drivers/ieee1394/dv1394.h +++ /dev/null @@ -1,305 +0,0 @@ -/* - * dv1394.h - DV input/output over IEEE 1394 on OHCI chips - * Copyright (C)2001 Daniel Maas - * receive by Dan Dennedy - * - * based on: - * video1394.h - driver for OHCI 1394 boards - * Copyright (C)1999,2000 Sebastien Rougeaux - * Peter Schlaile - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _DV_1394_H -#define _DV_1394_H - -/* This is the public user-space interface. Try not to break it. */ - -#define DV1394_API_VERSION 0x20011127 - -/* ******************** - ** ** - ** DV1394 API ** - ** ** - ******************** - - There are two methods of operating the DV1394 DV output device. - - 1) - - The simplest is an interface based on write(): simply write - full DV frames of data to the device, and they will be transmitted - as quickly as possible. The FD may be set for non-blocking I/O, - in which case you can use select() or poll() to wait for output - buffer space. - - To set the DV output parameters (e.g. whether you want NTSC or PAL - video), use the DV1394_INIT ioctl, passing in the parameters you - want in a struct dv1394_init. - - Example 1: - To play a raw .DV file: cat foo.DV > /dev/dv1394 - (cat will use write() internally) - - Example 2: - static struct dv1394_init init = { - 0x63, (broadcast channel) - 4, (four-frame ringbuffer) - DV1394_NTSC, (send NTSC video) - 0, 0 (default empty packet rate) - } - - ioctl(fd, DV1394_INIT, &init); - - while (1) { - read( , buf, DV1394_NTSC_FRAME_SIZE ); - write( , buf, DV1394_NTSC_FRAME_SIZE ); - } - - 2) - - For more control over buffering, and to avoid unnecessary copies - of the DV data, you can use the more sophisticated the mmap() interface. - First, call the DV1394_INIT ioctl to specify your parameters, - including the number of frames in the ringbuffer. Then, calling mmap() - on the dv1394 device will give you direct access to the ringbuffer - from which the DV card reads your frame data. - - The ringbuffer is simply one large, contiguous region of memory - containing two or more frames of packed DV data. Each frame of DV data - is 120000 bytes (NTSC) or 144000 bytes (PAL). - - Fill one or more frames in the ringbuffer, then use the DV1394_SUBMIT_FRAMES - ioctl to begin I/O. You can use either the DV1394_WAIT_FRAMES ioctl - or select()/poll() to wait until the frames are transmitted. Next, you'll - need to call the DV1394_GET_STATUS ioctl to determine which ringbuffer - frames are clear (ready to be filled with new DV data). Finally, use - DV1394_SUBMIT_FRAMES again to send the new data to the DV output. - - - Example: here is what a four-frame ringbuffer might look like - during DV transmission: - - - frame 0 frame 1 frame 2 frame 3 - - *--------------------------------------* - | CLEAR | DV data | DV data | CLEAR | - *--------------------------------------* - - - transmission goes in this direction --->>> - - - The DV hardware is currently transmitting the data in frame 1. - Once frame 1 is finished, it will automatically transmit frame 2. - (if frame 2 finishes before frame 3 is submitted, the device - will continue to transmit frame 2, and will increase the dropped_frames - counter each time it repeats the transmission). - - - If you called DV1394_GET_STATUS at this instant, you would - receive the following values: - - n_frames = 4 - active_frame = 1 - first_clear_frame = 3 - n_clear_frames = 2 - - At this point, you should write new DV data into frame 3 and optionally - frame 0. Then call DV1394_SUBMIT_FRAMES to inform the device that - it may transmit the new frames. - - ERROR HANDLING - - An error (buffer underflow/overflow or a break in the DV stream due - to a 1394 bus reset) can be detected by checking the dropped_frames - field of struct dv1394_status (obtained through the - DV1394_GET_STATUS ioctl). - - The best way to recover from such an error is to re-initialize - dv1394, either by using the DV1394_INIT ioctl call, or closing the - file descriptor and opening it again. (note that you must unmap all - ringbuffer mappings when closing the file descriptor, or else - dv1394 will still be considered 'in use'). - - MAIN LOOP - - For maximum efficiency and robustness against bus errors, you are - advised to model the main loop of your application after the - following pseudo-code example: - - (checks of system call return values omitted for brevity; always - check return values in your code!) - - while ( frames left ) { - - struct pollfd *pfd = ...; - - pfd->fd = dv1394_fd; - pfd->revents = 0; - pfd->events = POLLOUT | POLLIN; (OUT for transmit, IN for receive) - - (add other sources of I/O here) - - poll(pfd, 1, -1); (or select(); add a timeout if you want) - - if (pfd->revents) { - struct dv1394_status status; - - ioctl(dv1394_fd, DV1394_GET_STATUS, &status); - - if (status.dropped_frames > 0) { - reset_dv1394(); - } else { - for (int i = 0; i < status.n_clear_frames; i++) { - copy_DV_frame(); - } - } - } - } - - where copy_DV_frame() reads or writes on the dv1394 file descriptor - (read/write mode) or copies data to/from the mmap ringbuffer and - then calls ioctl(DV1394_SUBMIT_FRAMES) to notify dv1394 that new - frames are availble (mmap mode). - - reset_dv1394() is called in the event of a buffer - underflow/overflow or a halt in the DV stream (e.g. due to a 1394 - bus reset). To guarantee recovery from the error, this function - should close the dv1394 file descriptor (and munmap() all - ringbuffer mappings, if you are using them), then re-open the - dv1394 device (and re-map the ringbuffer). - -*/ - - -/* maximum number of frames in the ringbuffer */ -#define DV1394_MAX_FRAMES 32 - -/* number of *full* isochronous packets per DV frame */ -#define DV1394_NTSC_PACKETS_PER_FRAME 250 -#define DV1394_PAL_PACKETS_PER_FRAME 300 - -/* size of one frame's worth of DV data, in bytes */ -#define DV1394_NTSC_FRAME_SIZE (480 * DV1394_NTSC_PACKETS_PER_FRAME) -#define DV1394_PAL_FRAME_SIZE (480 * DV1394_PAL_PACKETS_PER_FRAME) - - -/* ioctl() commands */ -#include "ieee1394-ioctl.h" - - -enum pal_or_ntsc { - DV1394_NTSC = 0, - DV1394_PAL -}; - - - - -/* this is the argument to DV1394_INIT */ -struct dv1394_init { - /* DV1394_API_VERSION */ - unsigned int api_version; - - /* isochronous transmission channel to use */ - unsigned int channel; - - /* number of frames in the ringbuffer. Must be at least 2 - and at most DV1394_MAX_FRAMES. */ - unsigned int n_frames; - - /* send/receive PAL or NTSC video format */ - enum pal_or_ntsc format; - - /* the following are used only for transmission */ - - /* set these to zero unless you want a - non-default empty packet rate (see below) */ - unsigned long cip_n; - unsigned long cip_d; - - /* set this to zero unless you want a - non-default SYT cycle offset (default = 3 cycles) */ - unsigned int syt_offset; -}; - -/* NOTE: you may only allocate the DV frame ringbuffer once each time - you open the dv1394 device. DV1394_INIT will fail if you call it a - second time with different 'n_frames' or 'format' arguments (which - would imply a different size for the ringbuffer). If you need a - different buffer size, simply close and re-open the device, then - initialize it with your new settings. */ - -/* Q: What are cip_n and cip_d? */ - -/* - A: DV video streams do not utilize 100% of the potential bandwidth offered - by IEEE 1394 (FireWire). To achieve the correct rate of data transmission, - DV devices must periodically insert empty packets into the 1394 data stream. - Typically there is one empty packet per 14-16 data-carrying packets. - - Some DV devices will accept a wide range of empty packet rates, while others - require a precise rate. If the dv1394 driver produces empty packets at - a rate that your device does not accept, you may see ugly patterns on the - DV output, or even no output at all. - - The default empty packet insertion rate seems to work for many people; if - your DV output is stable, you can simply ignore this discussion. However, - we have exposed the empty packet rate as a parameter to support devices that - do not work with the default rate. - - The decision to insert an empty packet is made with a numerator/denominator - algorithm. Empty packets are produced at an average rate of CIP_N / CIP_D. - You can alter the empty packet rate by passing non-zero values for cip_n - and cip_d to the INIT ioctl. - - */ - - - -struct dv1394_status { - /* this embedded init struct returns the current dv1394 - parameters in use */ - struct dv1394_init init; - - /* the ringbuffer frame that is currently being - displayed. (-1 if the device is not transmitting anything) */ - int active_frame; - - /* index of the first buffer (ahead of active_frame) that - is ready to be filled with data */ - unsigned int first_clear_frame; - - /* how many buffers, including first_clear_buffer, are - ready to be filled with data */ - unsigned int n_clear_frames; - - /* how many times the DV stream has underflowed, overflowed, - or otherwise encountered an error, since the previous call - to DV1394_GET_STATUS */ - unsigned int dropped_frames; - - /* N.B. The dropped_frames counter is only a lower bound on the actual - number of dropped frames, with the special case that if dropped_frames - is zero, then it is guaranteed that NO frames have been dropped - since the last call to DV1394_GET_STATUS. - */ -}; - - -#endif /* _DV_1394_H */ diff --git a/drivers/ieee1394/eth1394.c b/drivers/ieee1394/eth1394.c deleted file mode 100644 index bc289e367e30..000000000000 --- a/drivers/ieee1394/eth1394.c +++ /dev/null @@ -1,1736 +0,0 @@ -/* - * eth1394.c -- IPv4 driver for Linux IEEE-1394 Subsystem - * - * Copyright (C) 2001-2003 Ben Collins - * 2000 Bonin Franck - * 2003 Steve Kinneberg - * - * Mainly based on work by Emanuel Pirker and Andreas E. Bombe - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - * This driver intends to support RFC 2734, which describes a method for - * transporting IPv4 datagrams over IEEE-1394 serial busses. - * - * TODO: - * RFC 2734 related: - * - Add MCAP. Limited Multicast exists only to 224.0.0.1 and 224.0.0.2. - * - * Non-RFC 2734 related: - * - Handle fragmented skb's coming from the networking layer. - * - Move generic GASP reception to core 1394 code - * - Convert kmalloc/kfree for link fragments to use kmem_cache_* instead - * - Stability improvements - * - Performance enhancements - * - Consider garbage collecting old partial datagrams after X amount of time - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config_roms.h" -#include "csr1212.h" -#include "eth1394.h" -#include "highlevel.h" -#include "ieee1394.h" -#include "ieee1394_core.h" -#include "ieee1394_hotplug.h" -#include "ieee1394_transactions.h" -#include "ieee1394_types.h" -#include "iso.h" -#include "nodemgr.h" - -#define ETH1394_PRINT_G(level, fmt, args...) \ - printk(level "%s: " fmt, driver_name, ## args) - -#define ETH1394_PRINT(level, dev_name, fmt, args...) \ - printk(level "%s: %s: " fmt, driver_name, dev_name, ## args) - -struct fragment_info { - struct list_head list; - int offset; - int len; -}; - -struct partial_datagram { - struct list_head list; - u16 dgl; - u16 dg_size; - __be16 ether_type; - struct sk_buff *skb; - char *pbuf; - struct list_head frag_info; -}; - -struct pdg_list { - struct list_head list; /* partial datagram list per node */ - unsigned int sz; /* partial datagram list size per node */ - spinlock_t lock; /* partial datagram lock */ -}; - -struct eth1394_host_info { - struct hpsb_host *host; - struct net_device *dev; -}; - -struct eth1394_node_ref { - struct unit_directory *ud; - struct list_head list; -}; - -struct eth1394_node_info { - u16 maxpayload; /* max payload */ - u8 sspd; /* max speed */ - u64 fifo; /* FIFO address */ - struct pdg_list pdg; /* partial RX datagram lists */ - int dgl; /* outgoing datagram label */ -}; - -static const char driver_name[] = "eth1394"; - -static struct kmem_cache *packet_task_cache; - -static struct hpsb_highlevel eth1394_highlevel; - -/* Use common.lf to determine header len */ -static const int hdr_type_len[] = { - sizeof(struct eth1394_uf_hdr), - sizeof(struct eth1394_ff_hdr), - sizeof(struct eth1394_sf_hdr), - sizeof(struct eth1394_sf_hdr) -}; - -static const u16 eth1394_speedto_maxpayload[] = { -/* S100, S200, S400, S800, S1600, S3200 */ - 512, 1024, 2048, 4096, 4096, 4096 -}; - -MODULE_AUTHOR("Ben Collins (bcollins@debian.org)"); -MODULE_DESCRIPTION("IEEE 1394 IPv4 Driver (IPv4-over-1394 as per RFC 2734)"); -MODULE_LICENSE("GPL"); - -/* - * The max_partial_datagrams parameter is the maximum number of fragmented - * datagrams per node that eth1394 will keep in memory. Providing an upper - * bound allows us to limit the amount of memory that partial datagrams - * consume in the event that some partial datagrams are never completed. - */ -static int max_partial_datagrams = 25; -module_param(max_partial_datagrams, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(max_partial_datagrams, - "Maximum number of partially received fragmented datagrams " - "(default = 25)."); - - -static int ether1394_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *daddr, - const void *saddr, unsigned len); -static int ether1394_rebuild_header(struct sk_buff *skb); -static int ether1394_header_parse(const struct sk_buff *skb, - unsigned char *haddr); -static int ether1394_header_cache(const struct neighbour *neigh, - struct hh_cache *hh); -static void ether1394_header_cache_update(struct hh_cache *hh, - const struct net_device *dev, - const unsigned char *haddr); -static netdev_tx_t ether1394_tx(struct sk_buff *skb, - struct net_device *dev); -static void ether1394_iso(struct hpsb_iso *iso); - -static const struct ethtool_ops ethtool_ops; - -static int ether1394_write(struct hpsb_host *host, int srcid, int destid, - quadlet_t *data, u64 addr, size_t len, u16 flags); -static void ether1394_add_host(struct hpsb_host *host); -static void ether1394_remove_host(struct hpsb_host *host); -static void ether1394_host_reset(struct hpsb_host *host); - -/* Function for incoming 1394 packets */ -static const struct hpsb_address_ops addr_ops = { - .write = ether1394_write, -}; - -/* Ieee1394 highlevel driver functions */ -static struct hpsb_highlevel eth1394_highlevel = { - .name = driver_name, - .add_host = ether1394_add_host, - .remove_host = ether1394_remove_host, - .host_reset = ether1394_host_reset, -}; - -static int ether1394_recv_init(struct eth1394_priv *priv) -{ - unsigned int iso_buf_size; - - /* FIXME: rawiso limits us to PAGE_SIZE */ - iso_buf_size = min((unsigned int)PAGE_SIZE, - 2 * (1U << (priv->host->csr.max_rec + 1))); - - priv->iso = hpsb_iso_recv_init(priv->host, - ETHER1394_GASP_BUFFERS * iso_buf_size, - ETHER1394_GASP_BUFFERS, - priv->broadcast_channel, - HPSB_ISO_DMA_PACKET_PER_BUFFER, - 1, ether1394_iso); - if (priv->iso == NULL) { - ETH1394_PRINT_G(KERN_ERR, "Failed to allocate IR context\n"); - priv->bc_state = ETHER1394_BC_ERROR; - return -EAGAIN; - } - - if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0) - priv->bc_state = ETHER1394_BC_STOPPED; - else - priv->bc_state = ETHER1394_BC_RUNNING; - return 0; -} - -/* This is called after an "ifup" */ -static int ether1394_open(struct net_device *dev) -{ - struct eth1394_priv *priv = netdev_priv(dev); - int ret; - - if (priv->bc_state == ETHER1394_BC_ERROR) { - ret = ether1394_recv_init(priv); - if (ret) - return ret; - } - netif_start_queue(dev); - return 0; -} - -/* This is called after an "ifdown" */ -static int ether1394_stop(struct net_device *dev) -{ - /* flush priv->wake */ - flush_scheduled_work(); - - netif_stop_queue(dev); - return 0; -} - -/* FIXME: What to do if we timeout? I think a host reset is probably in order, - * so that's what we do. Should we increment the stat counters too? */ -static void ether1394_tx_timeout(struct net_device *dev) -{ - struct hpsb_host *host = - ((struct eth1394_priv *)netdev_priv(dev))->host; - - ETH1394_PRINT(KERN_ERR, dev->name, "Timeout, resetting host\n"); - ether1394_host_reset(host); -} - -static inline int ether1394_max_mtu(struct hpsb_host* host) -{ - return (1 << (host->csr.max_rec + 1)) - - sizeof(union eth1394_hdr) - ETHER1394_GASP_OVERHEAD; -} - -static int ether1394_change_mtu(struct net_device *dev, int new_mtu) -{ - int max_mtu; - - if (new_mtu < 68) - return -EINVAL; - - max_mtu = ether1394_max_mtu( - ((struct eth1394_priv *)netdev_priv(dev))->host); - if (new_mtu > max_mtu) { - ETH1394_PRINT(KERN_INFO, dev->name, - "Local node constrains MTU to %d\n", max_mtu); - return -ERANGE; - } - - dev->mtu = new_mtu; - return 0; -} - -static void purge_partial_datagram(struct list_head *old) -{ - struct partial_datagram *pd; - struct list_head *lh, *n; - struct fragment_info *fi; - - pd = list_entry(old, struct partial_datagram, list); - - list_for_each_safe(lh, n, &pd->frag_info) { - fi = list_entry(lh, struct fragment_info, list); - list_del(lh); - kfree(fi); - } - list_del(old); - kfree_skb(pd->skb); - kfree(pd); -} - -/****************************************** - * 1394 bus activity functions - ******************************************/ - -static struct eth1394_node_ref *eth1394_find_node(struct list_head *inl, - struct unit_directory *ud) -{ - struct eth1394_node_ref *node; - - list_for_each_entry(node, inl, list) - if (node->ud == ud) - return node; - - return NULL; -} - -static struct eth1394_node_ref *eth1394_find_node_guid(struct list_head *inl, - u64 guid) -{ - struct eth1394_node_ref *node; - - list_for_each_entry(node, inl, list) - if (node->ud->ne->guid == guid) - return node; - - return NULL; -} - -static struct eth1394_node_ref *eth1394_find_node_nodeid(struct list_head *inl, - nodeid_t nodeid) -{ - struct eth1394_node_ref *node; - - list_for_each_entry(node, inl, list) - if (node->ud->ne->nodeid == nodeid) - return node; - - return NULL; -} - -static int eth1394_new_node(struct eth1394_host_info *hi, - struct unit_directory *ud) -{ - struct eth1394_priv *priv; - struct eth1394_node_ref *new_node; - struct eth1394_node_info *node_info; - - new_node = kmalloc(sizeof(*new_node), GFP_KERNEL); - if (!new_node) - return -ENOMEM; - - node_info = kmalloc(sizeof(*node_info), GFP_KERNEL); - if (!node_info) { - kfree(new_node); - return -ENOMEM; - } - - spin_lock_init(&node_info->pdg.lock); - INIT_LIST_HEAD(&node_info->pdg.list); - node_info->pdg.sz = 0; - node_info->fifo = CSR1212_INVALID_ADDR_SPACE; - - dev_set_drvdata(&ud->device, node_info); - new_node->ud = ud; - - priv = netdev_priv(hi->dev); - list_add_tail(&new_node->list, &priv->ip_node_list); - return 0; -} - -static int eth1394_probe(struct device *dev) -{ - struct unit_directory *ud; - struct eth1394_host_info *hi; - - ud = container_of(dev, struct unit_directory, device); - hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); - if (!hi) - return -ENOENT; - - return eth1394_new_node(hi, ud); -} - -static int eth1394_remove(struct device *dev) -{ - struct unit_directory *ud; - struct eth1394_host_info *hi; - struct eth1394_priv *priv; - struct eth1394_node_ref *old_node; - struct eth1394_node_info *node_info; - struct list_head *lh, *n; - unsigned long flags; - - ud = container_of(dev, struct unit_directory, device); - hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); - if (!hi) - return -ENOENT; - - priv = netdev_priv(hi->dev); - - old_node = eth1394_find_node(&priv->ip_node_list, ud); - if (!old_node) - return 0; - - list_del(&old_node->list); - kfree(old_node); - - node_info = dev_get_drvdata(&ud->device); - - spin_lock_irqsave(&node_info->pdg.lock, flags); - /* The partial datagram list should be empty, but we'll just - * make sure anyway... */ - list_for_each_safe(lh, n, &node_info->pdg.list) - purge_partial_datagram(lh); - spin_unlock_irqrestore(&node_info->pdg.lock, flags); - - kfree(node_info); - dev_set_drvdata(&ud->device, NULL); - return 0; -} - -static int eth1394_update(struct unit_directory *ud) -{ - struct eth1394_host_info *hi; - struct eth1394_priv *priv; - struct eth1394_node_ref *node; - - hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); - if (!hi) - return -ENOENT; - - priv = netdev_priv(hi->dev); - node = eth1394_find_node(&priv->ip_node_list, ud); - if (node) - return 0; - - return eth1394_new_node(hi, ud); -} - -static const struct ieee1394_device_id eth1394_id_table[] = { - { - .match_flags = (IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION), - .specifier_id = ETHER1394_GASP_SPECIFIER_ID, - .version = ETHER1394_GASP_VERSION, - }, - {} -}; - -MODULE_DEVICE_TABLE(ieee1394, eth1394_id_table); - -static struct hpsb_protocol_driver eth1394_proto_driver = { - .name = driver_name, - .id_table = eth1394_id_table, - .update = eth1394_update, - .driver = { - .probe = eth1394_probe, - .remove = eth1394_remove, - }, -}; - -static void ether1394_reset_priv(struct net_device *dev, int set_mtu) -{ - unsigned long flags; - int i; - struct eth1394_priv *priv = netdev_priv(dev); - struct hpsb_host *host = priv->host; - u64 guid = get_unaligned((u64 *)&(host->csr.rom->bus_info_data[3])); - int max_speed = IEEE1394_SPEED_MAX; - - spin_lock_irqsave(&priv->lock, flags); - - memset(priv->ud_list, 0, sizeof(priv->ud_list)); - priv->bc_maxpayload = 512; - - /* Determine speed limit */ - /* FIXME: This is broken for nodes with link speed < PHY speed, - * and it is suboptimal for S200B...S800B hardware. - * The result of nodemgr's speed probe should be used somehow. */ - for (i = 0; i < host->node_count; i++) { - /* take care of S100B...S400B PHY ports */ - if (host->speed[i] == SELFID_SPEED_UNKNOWN) { - max_speed = IEEE1394_SPEED_100; - break; - } - if (max_speed > host->speed[i]) - max_speed = host->speed[i]; - } - priv->bc_sspd = max_speed; - - if (set_mtu) { - /* Use the RFC 2734 default 1500 octets or the maximum payload - * as initial MTU */ - dev->mtu = min(1500, ether1394_max_mtu(host)); - - /* Set our hardware address while we're at it */ - memcpy(dev->dev_addr, &guid, sizeof(u64)); - memset(dev->broadcast, 0xff, sizeof(u64)); - } - - spin_unlock_irqrestore(&priv->lock, flags); -} - -static const struct header_ops ether1394_header_ops = { - .create = ether1394_header, - .rebuild = ether1394_rebuild_header, - .cache = ether1394_header_cache, - .cache_update = ether1394_header_cache_update, - .parse = ether1394_header_parse, -}; - -static const struct net_device_ops ether1394_netdev_ops = { - .ndo_open = ether1394_open, - .ndo_stop = ether1394_stop, - .ndo_start_xmit = ether1394_tx, - .ndo_tx_timeout = ether1394_tx_timeout, - .ndo_change_mtu = ether1394_change_mtu, -}; - -static void ether1394_init_dev(struct net_device *dev) -{ - - dev->header_ops = ðer1394_header_ops; - dev->netdev_ops = ðer1394_netdev_ops; - - SET_ETHTOOL_OPS(dev, ðtool_ops); - - dev->watchdog_timeo = ETHER1394_TIMEOUT; - dev->flags = IFF_BROADCAST | IFF_MULTICAST; - dev->features = NETIF_F_HIGHDMA; - dev->addr_len = ETH1394_ALEN; - dev->hard_header_len = ETH1394_HLEN; - dev->type = ARPHRD_IEEE1394; - - /* FIXME: This value was copied from ether_setup(). Is it too much? */ - dev->tx_queue_len = 1000; -} - -/* - * Wake the queue up after commonly encountered transmit failure conditions are - * hopefully over. Currently only tlabel exhaustion is accounted for. - */ -static void ether1394_wake_queue(struct work_struct *work) -{ - struct eth1394_priv *priv; - struct hpsb_packet *packet; - - priv = container_of(work, struct eth1394_priv, wake); - packet = hpsb_alloc_packet(0); - - /* This is really bad, but unjam the queue anyway. */ - if (!packet) - goto out; - - packet->host = priv->host; - packet->node_id = priv->wake_node; - /* - * A transaction label is all we really want. If we get one, it almost - * always means we can get a lot more because the ieee1394 core recycled - * a whole batch of tlabels, at last. - */ - if (hpsb_get_tlabel(packet) == 0) - hpsb_free_tlabel(packet); - - hpsb_free_packet(packet); -out: - netif_wake_queue(priv->wake_dev); -} - -/* - * This function is called every time a card is found. It is generally called - * when the module is installed. This is where we add all of our ethernet - * devices. One for each host. - */ -static void ether1394_add_host(struct hpsb_host *host) -{ - struct eth1394_host_info *hi = NULL; - struct net_device *dev = NULL; - struct eth1394_priv *priv; - u64 fifo_addr; - - if (hpsb_config_rom_ip1394_add(host) != 0) { - ETH1394_PRINT_G(KERN_ERR, "Can't add IP-over-1394 ROM entry\n"); - return; - } - - fifo_addr = hpsb_allocate_and_register_addrspace( - ð1394_highlevel, host, &addr_ops, - ETHER1394_REGION_ADDR_LEN, ETHER1394_REGION_ADDR_LEN, - CSR1212_INVALID_ADDR_SPACE, CSR1212_INVALID_ADDR_SPACE); - if (fifo_addr == CSR1212_INVALID_ADDR_SPACE) { - ETH1394_PRINT_G(KERN_ERR, "Cannot register CSR space\n"); - hpsb_config_rom_ip1394_remove(host); - return; - } - - dev = alloc_netdev(sizeof(*priv), "eth%d", ether1394_init_dev); - if (dev == NULL) { - ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); - goto out; - } - - SET_NETDEV_DEV(dev, &host->device); - - priv = netdev_priv(dev); - INIT_LIST_HEAD(&priv->ip_node_list); - spin_lock_init(&priv->lock); - priv->host = host; - priv->local_fifo = fifo_addr; - INIT_WORK(&priv->wake, ether1394_wake_queue); - priv->wake_dev = dev; - - hi = hpsb_create_hostinfo(ð1394_highlevel, host, sizeof(*hi)); - if (hi == NULL) { - ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); - goto out; - } - - ether1394_reset_priv(dev, 1); - - if (register_netdev(dev)) { - ETH1394_PRINT_G(KERN_ERR, "Cannot register the driver\n"); - goto out; - } - - ETH1394_PRINT(KERN_INFO, dev->name, "IPv4 over IEEE 1394 (fw-host%d)\n", - host->id); - - hi->host = host; - hi->dev = dev; - - /* Ignore validity in hopes that it will be set in the future. It'll - * be checked when the eth device is opened. */ - priv->broadcast_channel = host->csr.broadcast_channel & 0x3f; - - ether1394_recv_init(priv); - return; -out: - if (dev) - free_netdev(dev); - if (hi) - hpsb_destroy_hostinfo(ð1394_highlevel, host); - hpsb_unregister_addrspace(ð1394_highlevel, host, fifo_addr); - hpsb_config_rom_ip1394_remove(host); -} - -/* Remove a card from our list */ -static void ether1394_remove_host(struct hpsb_host *host) -{ - struct eth1394_host_info *hi; - struct eth1394_priv *priv; - - hi = hpsb_get_hostinfo(ð1394_highlevel, host); - if (!hi) - return; - priv = netdev_priv(hi->dev); - hpsb_unregister_addrspace(ð1394_highlevel, host, priv->local_fifo); - hpsb_config_rom_ip1394_remove(host); - if (priv->iso) - hpsb_iso_shutdown(priv->iso); - unregister_netdev(hi->dev); - free_netdev(hi->dev); -} - -/* A bus reset happened */ -static void ether1394_host_reset(struct hpsb_host *host) -{ - struct eth1394_host_info *hi; - struct eth1394_priv *priv; - struct net_device *dev; - struct list_head *lh, *n; - struct eth1394_node_ref *node; - struct eth1394_node_info *node_info; - unsigned long flags; - - hi = hpsb_get_hostinfo(ð1394_highlevel, host); - - /* This can happen for hosts that we don't use */ - if (!hi) - return; - - dev = hi->dev; - priv = netdev_priv(dev); - - /* Reset our private host data, but not our MTU */ - netif_stop_queue(dev); - ether1394_reset_priv(dev, 0); - - list_for_each_entry(node, &priv->ip_node_list, list) { - node_info = dev_get_drvdata(&node->ud->device); - - spin_lock_irqsave(&node_info->pdg.lock, flags); - - list_for_each_safe(lh, n, &node_info->pdg.list) - purge_partial_datagram(lh); - - INIT_LIST_HEAD(&(node_info->pdg.list)); - node_info->pdg.sz = 0; - - spin_unlock_irqrestore(&node_info->pdg.lock, flags); - } - - netif_wake_queue(dev); -} - -/****************************************** - * HW Header net device functions - ******************************************/ -/* These functions have been adapted from net/ethernet/eth.c */ - -/* Create a fake MAC header for an arbitrary protocol layer. - * saddr=NULL means use device source address - * daddr=NULL means leave destination address (eg unresolved arp). */ -static int ether1394_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *daddr, - const void *saddr, unsigned len) -{ - struct eth1394hdr *eth = - (struct eth1394hdr *)skb_push(skb, ETH1394_HLEN); - - eth->h_proto = htons(type); - - if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { - memset(eth->h_dest, 0, dev->addr_len); - return dev->hard_header_len; - } - - if (daddr) { - memcpy(eth->h_dest, daddr, dev->addr_len); - return dev->hard_header_len; - } - - return -dev->hard_header_len; -} - -/* Rebuild the faked MAC header. This is called after an ARP - * (or in future other address resolution) has completed on this - * sk_buff. We now let ARP fill in the other fields. - * - * This routine CANNOT use cached dst->neigh! - * Really, it is used only when dst->neigh is wrong. - */ -static int ether1394_rebuild_header(struct sk_buff *skb) -{ - struct eth1394hdr *eth = (struct eth1394hdr *)skb->data; - - if (eth->h_proto == htons(ETH_P_IP)) - return arp_find((unsigned char *)ð->h_dest, skb); - - ETH1394_PRINT(KERN_DEBUG, skb->dev->name, - "unable to resolve type %04x addresses\n", - ntohs(eth->h_proto)); - return 0; -} - -static int ether1394_header_parse(const struct sk_buff *skb, - unsigned char *haddr) -{ - memcpy(haddr, skb->dev->dev_addr, ETH1394_ALEN); - return ETH1394_ALEN; -} - -static int ether1394_header_cache(const struct neighbour *neigh, - struct hh_cache *hh) -{ - __be16 type = hh->hh_type; - struct net_device *dev = neigh->dev; - struct eth1394hdr *eth = - (struct eth1394hdr *)((u8 *)hh->hh_data + 16 - ETH1394_HLEN); - - if (type == htons(ETH_P_802_3)) - return -1; - - eth->h_proto = type; - memcpy(eth->h_dest, neigh->ha, dev->addr_len); - - hh->hh_len = ETH1394_HLEN; - return 0; -} - -/* Called by Address Resolution module to notify changes in address. */ -static void ether1394_header_cache_update(struct hh_cache *hh, - const struct net_device *dev, - const unsigned char * haddr) -{ - memcpy((u8 *)hh->hh_data + 16 - ETH1394_HLEN, haddr, dev->addr_len); -} - -/****************************************** - * Datagram reception code - ******************************************/ - -/* Copied from net/ethernet/eth.c */ -static __be16 ether1394_type_trans(struct sk_buff *skb, struct net_device *dev) -{ - struct eth1394hdr *eth; - unsigned char *rawp; - - skb_reset_mac_header(skb); - skb_pull(skb, ETH1394_HLEN); - eth = eth1394_hdr(skb); - - if (*eth->h_dest & 1) { - if (memcmp(eth->h_dest, dev->broadcast, dev->addr_len) == 0) - skb->pkt_type = PACKET_BROADCAST; -#if 0 - else - skb->pkt_type = PACKET_MULTICAST; -#endif - } else { - if (memcmp(eth->h_dest, dev->dev_addr, dev->addr_len)) - skb->pkt_type = PACKET_OTHERHOST; - } - - if (ntohs(eth->h_proto) >= 1536) - return eth->h_proto; - - rawp = skb->data; - - if (*(unsigned short *)rawp == 0xFFFF) - return htons(ETH_P_802_3); - - return htons(ETH_P_802_2); -} - -/* Parse an encapsulated IP1394 header into an ethernet frame packet. - * We also perform ARP translation here, if need be. */ -static __be16 ether1394_parse_encap(struct sk_buff *skb, struct net_device *dev, - nodeid_t srcid, nodeid_t destid, - __be16 ether_type) -{ - struct eth1394_priv *priv = netdev_priv(dev); - __be64 dest_hw; - __be16 ret = 0; - - /* Setup our hw addresses. We use these to build the ethernet header. */ - if (destid == (LOCAL_BUS | ALL_NODES)) - dest_hw = ~cpu_to_be64(0); /* broadcast */ - else - dest_hw = cpu_to_be64((u64)priv->host->csr.guid_hi << 32 | - priv->host->csr.guid_lo); - - /* If this is an ARP packet, convert it. First, we want to make - * use of some of the fields, since they tell us a little bit - * about the sending machine. */ - if (ether_type == htons(ETH_P_ARP)) { - struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data; - struct arphdr *arp = (struct arphdr *)skb->data; - unsigned char *arp_ptr = (unsigned char *)(arp + 1); - u64 fifo_addr = (u64)ntohs(arp1394->fifo_hi) << 32 | - ntohl(arp1394->fifo_lo); - u8 max_rec = min(priv->host->csr.max_rec, - (u8)(arp1394->max_rec)); - int sspd = arp1394->sspd; - u16 maxpayload; - struct eth1394_node_ref *node; - struct eth1394_node_info *node_info; - __be64 guid; - - /* Sanity check. MacOSX seems to be sending us 131 in this - * field (atleast on my Panther G5). Not sure why. */ - if (sspd > 5 || sspd < 0) - sspd = 0; - - maxpayload = min(eth1394_speedto_maxpayload[sspd], - (u16)(1 << (max_rec + 1))); - - guid = get_unaligned(&arp1394->s_uniq_id); - node = eth1394_find_node_guid(&priv->ip_node_list, - be64_to_cpu(guid)); - if (!node) - return cpu_to_be16(0); - - node_info = dev_get_drvdata(&node->ud->device); - - /* Update our speed/payload/fifo_offset table */ - node_info->maxpayload = maxpayload; - node_info->sspd = sspd; - node_info->fifo = fifo_addr; - - /* Now that we're done with the 1394 specific stuff, we'll - * need to alter some of the data. Believe it or not, all - * that needs to be done is sender_IP_address needs to be - * moved, the destination hardware address get stuffed - * in and the hardware address length set to 8. - * - * IMPORTANT: The code below overwrites 1394 specific data - * needed above so keep the munging of the data for the - * higher level IP stack last. */ - - arp->ar_hln = 8; - arp_ptr += arp->ar_hln; /* skip over sender unique id */ - *(u32 *)arp_ptr = arp1394->sip; /* move sender IP addr */ - arp_ptr += arp->ar_pln; /* skip over sender IP addr */ - - if (arp->ar_op == htons(ARPOP_REQUEST)) - memset(arp_ptr, 0, sizeof(u64)); - else - memcpy(arp_ptr, dev->dev_addr, sizeof(u64)); - } - - /* Now add the ethernet header. */ - if (dev_hard_header(skb, dev, ntohs(ether_type), &dest_hw, NULL, - skb->len) >= 0) - ret = ether1394_type_trans(skb, dev); - - return ret; -} - -static int fragment_overlap(struct list_head *frag_list, int offset, int len) -{ - struct fragment_info *fi; - int end = offset + len; - - list_for_each_entry(fi, frag_list, list) - if (offset < fi->offset + fi->len && end > fi->offset) - return 1; - - return 0; -} - -static struct list_head *find_partial_datagram(struct list_head *pdgl, int dgl) -{ - struct partial_datagram *pd; - - list_for_each_entry(pd, pdgl, list) - if (pd->dgl == dgl) - return &pd->list; - - return NULL; -} - -/* Assumes that new fragment does not overlap any existing fragments */ -static int new_fragment(struct list_head *frag_info, int offset, int len) -{ - struct list_head *lh; - struct fragment_info *fi, *fi2, *new; - - list_for_each(lh, frag_info) { - fi = list_entry(lh, struct fragment_info, list); - if (fi->offset + fi->len == offset) { - /* The new fragment can be tacked on to the end */ - fi->len += len; - /* Did the new fragment plug a hole? */ - fi2 = list_entry(lh->next, struct fragment_info, list); - if (fi->offset + fi->len == fi2->offset) { - /* glue fragments together */ - fi->len += fi2->len; - list_del(lh->next); - kfree(fi2); - } - return 0; - } else if (offset + len == fi->offset) { - /* The new fragment can be tacked on to the beginning */ - fi->offset = offset; - fi->len += len; - /* Did the new fragment plug a hole? */ - fi2 = list_entry(lh->prev, struct fragment_info, list); - if (fi2->offset + fi2->len == fi->offset) { - /* glue fragments together */ - fi2->len += fi->len; - list_del(lh); - kfree(fi); - } - return 0; - } else if (offset > fi->offset + fi->len) { - break; - } else if (offset + len < fi->offset) { - lh = lh->prev; - break; - } - } - - new = kmalloc(sizeof(*new), GFP_ATOMIC); - if (!new) - return -ENOMEM; - - new->offset = offset; - new->len = len; - - list_add(&new->list, lh); - return 0; -} - -static int new_partial_datagram(struct net_device *dev, struct list_head *pdgl, - int dgl, int dg_size, char *frag_buf, - int frag_off, int frag_len) -{ - struct partial_datagram *new; - - new = kmalloc(sizeof(*new), GFP_ATOMIC); - if (!new) - return -ENOMEM; - - INIT_LIST_HEAD(&new->frag_info); - - if (new_fragment(&new->frag_info, frag_off, frag_len) < 0) { - kfree(new); - return -ENOMEM; - } - - new->dgl = dgl; - new->dg_size = dg_size; - - new->skb = dev_alloc_skb(dg_size + dev->hard_header_len + 15); - if (!new->skb) { - struct fragment_info *fi = list_entry(new->frag_info.next, - struct fragment_info, - list); - kfree(fi); - kfree(new); - return -ENOMEM; - } - - skb_reserve(new->skb, (dev->hard_header_len + 15) & ~15); - new->pbuf = skb_put(new->skb, dg_size); - memcpy(new->pbuf + frag_off, frag_buf, frag_len); - - list_add(&new->list, pdgl); - return 0; -} - -static int update_partial_datagram(struct list_head *pdgl, struct list_head *lh, - char *frag_buf, int frag_off, int frag_len) -{ - struct partial_datagram *pd = - list_entry(lh, struct partial_datagram, list); - - if (new_fragment(&pd->frag_info, frag_off, frag_len) < 0) - return -ENOMEM; - - memcpy(pd->pbuf + frag_off, frag_buf, frag_len); - - /* Move list entry to beginnig of list so that oldest partial - * datagrams percolate to the end of the list */ - list_move(lh, pdgl); - return 0; -} - -static int is_datagram_complete(struct list_head *lh, int dg_size) -{ - struct partial_datagram *pd; - struct fragment_info *fi; - - pd = list_entry(lh, struct partial_datagram, list); - fi = list_entry(pd->frag_info.next, struct fragment_info, list); - - return (fi->len == dg_size); -} - -/* Packet reception. We convert the IP1394 encapsulation header to an - * ethernet header, and fill it with some of our other fields. This is - * an incoming packet from the 1394 bus. */ -static int ether1394_data_handler(struct net_device *dev, int srcid, int destid, - char *buf, int len) -{ - struct sk_buff *skb; - unsigned long flags; - struct eth1394_priv *priv = netdev_priv(dev); - union eth1394_hdr *hdr = (union eth1394_hdr *)buf; - __be16 ether_type = cpu_to_be16(0); /* initialized to clear warning */ - int hdr_len; - struct unit_directory *ud = priv->ud_list[NODEID_TO_NODE(srcid)]; - struct eth1394_node_info *node_info; - - if (!ud) { - struct eth1394_node_ref *node; - node = eth1394_find_node_nodeid(&priv->ip_node_list, srcid); - if (unlikely(!node)) { - HPSB_PRINT(KERN_ERR, "ether1394 rx: sender nodeid " - "lookup failure: " NODE_BUS_FMT, - NODE_BUS_ARGS(priv->host, srcid)); - dev->stats.rx_dropped++; - return -1; - } - ud = node->ud; - - priv->ud_list[NODEID_TO_NODE(srcid)] = ud; - } - - node_info = dev_get_drvdata(&ud->device); - - /* First, did we receive a fragmented or unfragmented datagram? */ - hdr->words.word1 = ntohs(hdr->words.word1); - - hdr_len = hdr_type_len[hdr->common.lf]; - - if (hdr->common.lf == ETH1394_HDR_LF_UF) { - /* An unfragmented datagram has been received by the ieee1394 - * bus. Build an skbuff around it so we can pass it to the - * high level network layer. */ - - skb = dev_alloc_skb(len + dev->hard_header_len + 15); - if (unlikely(!skb)) { - ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); - dev->stats.rx_dropped++; - return -1; - } - skb_reserve(skb, (dev->hard_header_len + 15) & ~15); - memcpy(skb_put(skb, len - hdr_len), buf + hdr_len, - len - hdr_len); - ether_type = hdr->uf.ether_type; - } else { - /* A datagram fragment has been received, now the fun begins. */ - - struct list_head *pdgl, *lh; - struct partial_datagram *pd; - int fg_off; - int fg_len = len - hdr_len; - int dg_size; - int dgl; - int retval; - struct pdg_list *pdg = &(node_info->pdg); - - hdr->words.word3 = ntohs(hdr->words.word3); - /* The 4th header word is reserved so no need to do ntohs() */ - - if (hdr->common.lf == ETH1394_HDR_LF_FF) { - ether_type = hdr->ff.ether_type; - dgl = hdr->ff.dgl; - dg_size = hdr->ff.dg_size + 1; - fg_off = 0; - } else { - hdr->words.word2 = ntohs(hdr->words.word2); - dgl = hdr->sf.dgl; - dg_size = hdr->sf.dg_size + 1; - fg_off = hdr->sf.fg_off; - } - spin_lock_irqsave(&pdg->lock, flags); - - pdgl = &(pdg->list); - lh = find_partial_datagram(pdgl, dgl); - - if (lh == NULL) { - while (pdg->sz >= max_partial_datagrams) { - /* remove the oldest */ - purge_partial_datagram(pdgl->prev); - pdg->sz--; - } - - retval = new_partial_datagram(dev, pdgl, dgl, dg_size, - buf + hdr_len, fg_off, - fg_len); - if (retval < 0) { - spin_unlock_irqrestore(&pdg->lock, flags); - goto bad_proto; - } - pdg->sz++; - lh = find_partial_datagram(pdgl, dgl); - } else { - pd = list_entry(lh, struct partial_datagram, list); - - if (fragment_overlap(&pd->frag_info, fg_off, fg_len)) { - /* Overlapping fragments, obliterate old - * datagram and start new one. */ - purge_partial_datagram(lh); - retval = new_partial_datagram(dev, pdgl, dgl, - dg_size, - buf + hdr_len, - fg_off, fg_len); - if (retval < 0) { - pdg->sz--; - spin_unlock_irqrestore(&pdg->lock, flags); - goto bad_proto; - } - } else { - retval = update_partial_datagram(pdgl, lh, - buf + hdr_len, - fg_off, fg_len); - if (retval < 0) { - /* Couldn't save off fragment anyway - * so might as well obliterate the - * datagram now. */ - purge_partial_datagram(lh); - pdg->sz--; - spin_unlock_irqrestore(&pdg->lock, flags); - goto bad_proto; - } - } /* fragment overlap */ - } /* new datagram or add to existing one */ - - pd = list_entry(lh, struct partial_datagram, list); - - if (hdr->common.lf == ETH1394_HDR_LF_FF) - pd->ether_type = ether_type; - - if (is_datagram_complete(lh, dg_size)) { - ether_type = pd->ether_type; - pdg->sz--; - skb = skb_get(pd->skb); - purge_partial_datagram(lh); - spin_unlock_irqrestore(&pdg->lock, flags); - } else { - /* Datagram is not complete, we're done for the - * moment. */ - spin_unlock_irqrestore(&pdg->lock, flags); - return 0; - } - } /* unframgented datagram or fragmented one */ - - /* Write metadata, and then pass to the receive level */ - skb->dev = dev; - skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ - - /* Parse the encapsulation header. This actually does the job of - * converting to an ethernet frame header, aswell as arp - * conversion if needed. ARP conversion is easier in this - * direction, since we are using ethernet as our backend. */ - skb->protocol = ether1394_parse_encap(skb, dev, srcid, destid, - ether_type); - - spin_lock_irqsave(&priv->lock, flags); - - if (!skb->protocol) { - dev->stats.rx_errors++; - dev->stats.rx_dropped++; - dev_kfree_skb_any(skb); - } else if (netif_rx(skb) == NET_RX_DROP) { - dev->stats.rx_errors++; - dev->stats.rx_dropped++; - } else { - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - } - - spin_unlock_irqrestore(&priv->lock, flags); - -bad_proto: - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); - - return 0; -} - -static int ether1394_write(struct hpsb_host *host, int srcid, int destid, - quadlet_t *data, u64 addr, size_t len, u16 flags) -{ - struct eth1394_host_info *hi; - - hi = hpsb_get_hostinfo(ð1394_highlevel, host); - if (unlikely(!hi)) { - ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n", - host->id); - return RCODE_ADDRESS_ERROR; - } - - if (ether1394_data_handler(hi->dev, srcid, destid, (char*)data, len)) - return RCODE_ADDRESS_ERROR; - else - return RCODE_COMPLETE; -} - -static void ether1394_iso(struct hpsb_iso *iso) -{ - __be32 *data; - char *buf; - struct eth1394_host_info *hi; - struct net_device *dev; - unsigned int len; - u32 specifier_id; - u16 source_id; - int i; - int nready; - - hi = hpsb_get_hostinfo(ð1394_highlevel, iso->host); - if (unlikely(!hi)) { - ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n", - iso->host->id); - return; - } - - dev = hi->dev; - - nready = hpsb_iso_n_ready(iso); - for (i = 0; i < nready; i++) { - struct hpsb_iso_packet_info *info = - &iso->infos[(iso->first_packet + i) % iso->buf_packets]; - data = (__be32 *)(iso->data_buf.kvirt + info->offset); - - /* skip over GASP header */ - buf = (char *)data + 8; - len = info->len - 8; - - specifier_id = (be32_to_cpu(data[0]) & 0xffff) << 8 | - (be32_to_cpu(data[1]) & 0xff000000) >> 24; - source_id = be32_to_cpu(data[0]) >> 16; - - if (info->channel != (iso->host->csr.broadcast_channel & 0x3f) - || specifier_id != ETHER1394_GASP_SPECIFIER_ID) { - /* This packet is not for us */ - continue; - } - ether1394_data_handler(dev, source_id, LOCAL_BUS | ALL_NODES, - buf, len); - } - - hpsb_iso_recv_release_packets(iso, i); - -} - -/****************************************** - * Datagram transmission code - ******************************************/ - -/* Convert a standard ARP packet to 1394 ARP. The first 8 bytes (the entire - * arphdr) is the same format as the ip1394 header, so they overlap. The rest - * needs to be munged a bit. The remainder of the arphdr is formatted based - * on hwaddr len and ipaddr len. We know what they'll be, so it's easy to - * judge. - * - * Now that the EUI is used for the hardware address all we need to do to make - * this work for 1394 is to insert 2 quadlets that contain max_rec size, - * speed, and unicast FIFO address information between the sender_unique_id - * and the IP addresses. - */ -static void ether1394_arp_to_1394arp(struct sk_buff *skb, - struct net_device *dev) -{ - struct eth1394_priv *priv = netdev_priv(dev); - struct arphdr *arp = (struct arphdr *)skb->data; - unsigned char *arp_ptr = (unsigned char *)(arp + 1); - struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data; - - arp1394->hw_addr_len = 16; - arp1394->sip = *(u32*)(arp_ptr + ETH1394_ALEN); - arp1394->max_rec = priv->host->csr.max_rec; - arp1394->sspd = priv->host->csr.lnk_spd; - arp1394->fifo_hi = htons(priv->local_fifo >> 32); - arp1394->fifo_lo = htonl(priv->local_fifo & ~0x0); -} - -/* We need to encapsulate the standard header with our own. We use the - * ethernet header's proto for our own. */ -static unsigned int ether1394_encapsulate_prep(unsigned int max_payload, - __be16 proto, - union eth1394_hdr *hdr, - u16 dg_size, u16 dgl) -{ - unsigned int adj_max_payload = - max_payload - hdr_type_len[ETH1394_HDR_LF_UF]; - - /* Does it all fit in one packet? */ - if (dg_size <= adj_max_payload) { - hdr->uf.lf = ETH1394_HDR_LF_UF; - hdr->uf.ether_type = proto; - } else { - hdr->ff.lf = ETH1394_HDR_LF_FF; - hdr->ff.ether_type = proto; - hdr->ff.dg_size = dg_size - 1; - hdr->ff.dgl = dgl; - adj_max_payload = max_payload - hdr_type_len[ETH1394_HDR_LF_FF]; - } - return DIV_ROUND_UP(dg_size, adj_max_payload); -} - -static unsigned int ether1394_encapsulate(struct sk_buff *skb, - unsigned int max_payload, - union eth1394_hdr *hdr) -{ - union eth1394_hdr *bufhdr; - int ftype = hdr->common.lf; - int hdrsz = hdr_type_len[ftype]; - unsigned int adj_max_payload = max_payload - hdrsz; - - switch (ftype) { - case ETH1394_HDR_LF_UF: - bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz); - bufhdr->words.word1 = htons(hdr->words.word1); - bufhdr->words.word2 = hdr->words.word2; - break; - - case ETH1394_HDR_LF_FF: - bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz); - bufhdr->words.word1 = htons(hdr->words.word1); - bufhdr->words.word2 = hdr->words.word2; - bufhdr->words.word3 = htons(hdr->words.word3); - bufhdr->words.word4 = 0; - - /* Set frag type here for future interior fragments */ - hdr->common.lf = ETH1394_HDR_LF_IF; - hdr->sf.fg_off = 0; - break; - - default: - hdr->sf.fg_off += adj_max_payload; - bufhdr = (union eth1394_hdr *)skb_pull(skb, adj_max_payload); - if (max_payload >= skb->len) - hdr->common.lf = ETH1394_HDR_LF_LF; - bufhdr->words.word1 = htons(hdr->words.word1); - bufhdr->words.word2 = htons(hdr->words.word2); - bufhdr->words.word3 = htons(hdr->words.word3); - bufhdr->words.word4 = 0; - } - return min(max_payload, skb->len); -} - -static struct hpsb_packet *ether1394_alloc_common_packet(struct hpsb_host *host) -{ - struct hpsb_packet *p; - - p = hpsb_alloc_packet(0); - if (p) { - p->host = host; - p->generation = get_hpsb_generation(host); - p->type = hpsb_async; - } - return p; -} - -static int ether1394_prep_write_packet(struct hpsb_packet *p, - struct hpsb_host *host, nodeid_t node, - u64 addr, void *data, int tx_len) -{ - p->node_id = node; - - if (hpsb_get_tlabel(p)) - return -EAGAIN; - - p->tcode = TCODE_WRITEB; - p->header_size = 16; - p->expect_response = 1; - p->header[0] = - p->node_id << 16 | p->tlabel << 10 | 1 << 8 | TCODE_WRITEB << 4; - p->header[1] = host->node_id << 16 | addr >> 32; - p->header[2] = addr & 0xffffffff; - p->header[3] = tx_len << 16; - p->data_size = (tx_len + 3) & ~3; - p->data = data; - - return 0; -} - -static void ether1394_prep_gasp_packet(struct hpsb_packet *p, - struct eth1394_priv *priv, - struct sk_buff *skb, int length) -{ - p->header_size = 4; - p->tcode = TCODE_STREAM_DATA; - - p->header[0] = length << 16 | 3 << 14 | priv->broadcast_channel << 8 | - TCODE_STREAM_DATA << 4; - p->data_size = length; - p->data = (quadlet_t *)skb->data - 2; - p->data[0] = cpu_to_be32(priv->host->node_id << 16 | - ETHER1394_GASP_SPECIFIER_ID_HI); - p->data[1] = cpu_to_be32(ETHER1394_GASP_SPECIFIER_ID_LO << 24 | - ETHER1394_GASP_VERSION); - - p->speed_code = priv->bc_sspd; - - /* prevent hpsb_send_packet() from overriding our speed code */ - p->node_id = LOCAL_BUS | ALL_NODES; -} - -static void ether1394_free_packet(struct hpsb_packet *packet) -{ - if (packet->tcode != TCODE_STREAM_DATA) - hpsb_free_tlabel(packet); - hpsb_free_packet(packet); -} - -static void ether1394_complete_cb(void *__ptask); - -static int ether1394_send_packet(struct packet_task *ptask, unsigned int tx_len) -{ - struct eth1394_priv *priv = ptask->priv; - struct hpsb_packet *packet = NULL; - - packet = ether1394_alloc_common_packet(priv->host); - if (!packet) - return -ENOMEM; - - if (ptask->tx_type == ETH1394_GASP) { - int length = tx_len + 2 * sizeof(quadlet_t); - - ether1394_prep_gasp_packet(packet, priv, ptask->skb, length); - } else if (ether1394_prep_write_packet(packet, priv->host, - ptask->dest_node, - ptask->addr, ptask->skb->data, - tx_len)) { - hpsb_free_packet(packet); - return -EAGAIN; - } - - ptask->packet = packet; - hpsb_set_packet_complete_task(ptask->packet, ether1394_complete_cb, - ptask); - - if (hpsb_send_packet(packet) < 0) { - ether1394_free_packet(packet); - return -EIO; - } - - return 0; -} - -/* Task function to be run when a datagram transmission is completed */ -static void ether1394_dg_complete(struct packet_task *ptask, int fail) -{ - struct sk_buff *skb = ptask->skb; - struct net_device *dev = skb->dev; - struct eth1394_priv *priv = netdev_priv(dev); - unsigned long flags; - - /* Statistics */ - spin_lock_irqsave(&priv->lock, flags); - if (fail) { - dev->stats.tx_dropped++; - dev->stats.tx_errors++; - } else { - dev->stats.tx_bytes += skb->len; - dev->stats.tx_packets++; - } - spin_unlock_irqrestore(&priv->lock, flags); - - dev_kfree_skb_any(skb); - kmem_cache_free(packet_task_cache, ptask); -} - -/* Callback for when a packet has been sent and the status of that packet is - * known */ -static void ether1394_complete_cb(void *__ptask) -{ - struct packet_task *ptask = (struct packet_task *)__ptask; - struct hpsb_packet *packet = ptask->packet; - int fail = 0; - - if (packet->tcode != TCODE_STREAM_DATA) - fail = hpsb_packet_success(packet); - - ether1394_free_packet(packet); - - ptask->outstanding_pkts--; - if (ptask->outstanding_pkts > 0 && !fail) { - int tx_len, err; - - /* Add the encapsulation header to the fragment */ - tx_len = ether1394_encapsulate(ptask->skb, ptask->max_payload, - &ptask->hdr); - err = ether1394_send_packet(ptask, tx_len); - if (err) { - if (err == -EAGAIN) - ETH1394_PRINT_G(KERN_ERR, "Out of tlabels\n"); - - ether1394_dg_complete(ptask, 1); - } - } else { - ether1394_dg_complete(ptask, fail); - } -} - -/* Transmit a packet (called by kernel) */ -static netdev_tx_t ether1394_tx(struct sk_buff *skb, - struct net_device *dev) -{ - struct eth1394hdr hdr_buf; - struct eth1394_priv *priv = netdev_priv(dev); - __be16 proto; - unsigned long flags; - nodeid_t dest_node; - eth1394_tx_type tx_type; - unsigned int tx_len; - unsigned int max_payload; - u16 dg_size; - u16 dgl; - struct packet_task *ptask; - struct eth1394_node_ref *node; - struct eth1394_node_info *node_info = NULL; - - ptask = kmem_cache_alloc(packet_task_cache, GFP_ATOMIC); - if (ptask == NULL) - goto fail; - - /* XXX Ignore this for now. Noticed that when MacOSX is the IRM, - * it does not set our validity bit. We need to compensate for - * that somewhere else, but not in eth1394. */ -#if 0 - if ((priv->host->csr.broadcast_channel & 0xc0000000) != 0xc0000000) - goto fail; -#endif - - skb = skb_share_check(skb, GFP_ATOMIC); - if (!skb) - goto fail; - - /* Get rid of the fake eth1394 header, but first make a copy. - * We might need to rebuild the header on tx failure. */ - memcpy(&hdr_buf, skb->data, sizeof(hdr_buf)); - skb_pull(skb, ETH1394_HLEN); - - proto = hdr_buf.h_proto; - dg_size = skb->len; - - /* Set the transmission type for the packet. ARP packets and IP - * broadcast packets are sent via GASP. */ - if (memcmp(hdr_buf.h_dest, dev->broadcast, ETH1394_ALEN) == 0 || - proto == htons(ETH_P_ARP) || - (proto == htons(ETH_P_IP) && - IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) { - tx_type = ETH1394_GASP; - dest_node = LOCAL_BUS | ALL_NODES; - max_payload = priv->bc_maxpayload - ETHER1394_GASP_OVERHEAD; - BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD); - dgl = priv->bc_dgl; - if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF]) - priv->bc_dgl++; - } else { - __be64 guid = get_unaligned((__be64 *)hdr_buf.h_dest); - - node = eth1394_find_node_guid(&priv->ip_node_list, - be64_to_cpu(guid)); - if (!node) - goto fail; - - node_info = dev_get_drvdata(&node->ud->device); - if (node_info->fifo == CSR1212_INVALID_ADDR_SPACE) - goto fail; - - dest_node = node->ud->ne->nodeid; - max_payload = node_info->maxpayload; - BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD); - - dgl = node_info->dgl; - if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF]) - node_info->dgl++; - tx_type = ETH1394_WRREQ; - } - - /* If this is an ARP packet, convert it */ - if (proto == htons(ETH_P_ARP)) - ether1394_arp_to_1394arp(skb, dev); - - ptask->hdr.words.word1 = 0; - ptask->hdr.words.word2 = 0; - ptask->hdr.words.word3 = 0; - ptask->hdr.words.word4 = 0; - ptask->skb = skb; - ptask->priv = priv; - ptask->tx_type = tx_type; - - if (tx_type != ETH1394_GASP) { - u64 addr; - - spin_lock_irqsave(&priv->lock, flags); - addr = node_info->fifo; - spin_unlock_irqrestore(&priv->lock, flags); - - ptask->addr = addr; - ptask->dest_node = dest_node; - } - - ptask->tx_type = tx_type; - ptask->max_payload = max_payload; - ptask->outstanding_pkts = ether1394_encapsulate_prep(max_payload, - proto, &ptask->hdr, dg_size, dgl); - - /* Add the encapsulation header to the fragment */ - tx_len = ether1394_encapsulate(skb, max_payload, &ptask->hdr); - dev->trans_start = jiffies; - if (ether1394_send_packet(ptask, tx_len)) { - if (dest_node == (LOCAL_BUS | ALL_NODES)) - goto fail; - - /* At this point we want to restore the packet. When we return - * here with NETDEV_TX_BUSY we will get another entrance in this - * routine with the same skb and we need it to look the same. - * So we pull 4 more bytes, then build the header again. */ - skb_pull(skb, 4); - ether1394_header(skb, dev, ntohs(hdr_buf.h_proto), - hdr_buf.h_dest, NULL, 0); - - /* Most failures of ether1394_send_packet are recoverable. */ - netif_stop_queue(dev); - priv->wake_node = dest_node; - schedule_work(&priv->wake); - kmem_cache_free(packet_task_cache, ptask); - return NETDEV_TX_BUSY; - } - - return NETDEV_TX_OK; -fail: - if (ptask) - kmem_cache_free(packet_task_cache, ptask); - - if (skb != NULL) - dev_kfree_skb(skb); - - spin_lock_irqsave(&priv->lock, flags); - dev->stats.tx_dropped++; - dev->stats.tx_errors++; - spin_unlock_irqrestore(&priv->lock, flags); - - return NETDEV_TX_OK; -} - -static void ether1394_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strcpy(info->driver, driver_name); - strcpy(info->bus_info, "ieee1394"); /* FIXME provide more detail? */ -} - -static const struct ethtool_ops ethtool_ops = { - .get_drvinfo = ether1394_get_drvinfo -}; - -static int __init ether1394_init_module(void) -{ - int err; - - packet_task_cache = kmem_cache_create("packet_task", - sizeof(struct packet_task), - 0, 0, NULL); - if (!packet_task_cache) - return -ENOMEM; - - hpsb_register_highlevel(ð1394_highlevel); - err = hpsb_register_protocol(ð1394_proto_driver); - if (err) { - hpsb_unregister_highlevel(ð1394_highlevel); - kmem_cache_destroy(packet_task_cache); - } - return err; -} - -static void __exit ether1394_exit_module(void) -{ - hpsb_unregister_protocol(ð1394_proto_driver); - hpsb_unregister_highlevel(ð1394_highlevel); - kmem_cache_destroy(packet_task_cache); -} - -module_init(ether1394_init_module); -module_exit(ether1394_exit_module); diff --git a/drivers/ieee1394/eth1394.h b/drivers/ieee1394/eth1394.h deleted file mode 100644 index d53bac47b86f..000000000000 --- a/drivers/ieee1394/eth1394.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * eth1394.h -- Ethernet driver for Linux IEEE-1394 Subsystem - * - * Copyright (C) 2000 Bonin Franck - * (C) 2001 Ben Collins - * - * Mainly based on work by Emanuel Pirker and Andreas E. Bombe - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef __ETH1394_H -#define __ETH1394_H - -#include -#include -#include - -#include "ieee1394.h" -#include "ieee1394_types.h" - -/* Register for incoming packets. This is 4096 bytes, which supports up to - * S3200 (per Table 16-3 of IEEE 1394b-2002). */ -#define ETHER1394_REGION_ADDR_LEN 4096 - -/* GASP identifier numbers for IPv4 over IEEE 1394 */ -#define ETHER1394_GASP_SPECIFIER_ID 0x00005E -#define ETHER1394_GASP_SPECIFIER_ID_HI ((0x00005E >> 8) & 0xffff) -#define ETHER1394_GASP_SPECIFIER_ID_LO (0x00005E & 0xff) -#define ETHER1394_GASP_VERSION 1 - -#define ETHER1394_GASP_OVERHEAD (2 * sizeof(quadlet_t)) /* for GASP header */ - -#define ETHER1394_GASP_BUFFERS 16 - -#define NODE_SET (ALL_NODES + 1) /* Node set == 64 */ - -enum eth1394_bc_states { ETHER1394_BC_ERROR, - ETHER1394_BC_RUNNING, - ETHER1394_BC_STOPPED }; - - -/* Private structure for our ethernet driver */ -struct eth1394_priv { - struct hpsb_host *host; /* The card for this dev */ - u16 bc_maxpayload; /* Max broadcast payload */ - u8 bc_sspd; /* Max broadcast speed */ - u64 local_fifo; /* Local FIFO Address */ - spinlock_t lock; /* Private lock */ - int broadcast_channel; /* Async stream Broadcast Channel */ - enum eth1394_bc_states bc_state; /* broadcast channel state */ - struct hpsb_iso *iso; /* Async stream recv handle */ - int bc_dgl; /* Outgoing broadcast datagram label */ - struct list_head ip_node_list; /* List of IP capable nodes */ - struct unit_directory *ud_list[ALL_NODES]; /* Cached unit dir list */ - - struct work_struct wake; /* Wake up after xmit failure */ - struct net_device *wake_dev; /* Stupid backlink for .wake */ - nodeid_t wake_node; /* Destination of failed xmit */ -}; - - -/* Define a fake hardware header format for the networking core. Note that - * header size cannot exceed 16 bytes as that is the size of the header cache. - * Also, we do not need the source address in the header so we omit it and - * keep the header to under 16 bytes */ -#define ETH1394_ALEN (8) -#define ETH1394_HLEN (10) - -struct eth1394hdr { - unsigned char h_dest[ETH1394_ALEN]; /* destination eth1394 addr */ - __be16 h_proto; /* packet type ID field */ -} __attribute__((packed)); - -static inline struct eth1394hdr *eth1394_hdr(const struct sk_buff *skb) -{ - return (struct eth1394hdr *)skb_mac_header(skb); -} - -typedef enum {ETH1394_GASP, ETH1394_WRREQ} eth1394_tx_type; - -/* IP1394 headers */ - -/* Unfragmented */ -#if defined __BIG_ENDIAN_BITFIELD -struct eth1394_uf_hdr { - u16 lf:2; - u16 res:14; - __be16 ether_type; /* Ethernet packet type */ -} __attribute__((packed)); -#elif defined __LITTLE_ENDIAN_BITFIELD -struct eth1394_uf_hdr { - u16 res:14; - u16 lf:2; - __be16 ether_type; -} __attribute__((packed)); -#else -#error Unknown bit field type -#endif - -/* First fragment */ -#if defined __BIG_ENDIAN_BITFIELD -struct eth1394_ff_hdr { - u16 lf:2; - u16 res1:2; - u16 dg_size:12; /* Datagram size */ - __be16 ether_type; /* Ethernet packet type */ - u16 dgl; /* Datagram label */ - u16 res2; -} __attribute__((packed)); -#elif defined __LITTLE_ENDIAN_BITFIELD -struct eth1394_ff_hdr { - u16 dg_size:12; - u16 res1:2; - u16 lf:2; - __be16 ether_type; - u16 dgl; - u16 res2; -} __attribute__((packed)); -#else -#error Unknown bit field type -#endif - -/* XXX: Subsequent fragments, including last */ -#if defined __BIG_ENDIAN_BITFIELD -struct eth1394_sf_hdr { - u16 lf:2; - u16 res1:2; - u16 dg_size:12; /* Datagram size */ - u16 res2:4; - u16 fg_off:12; /* Fragment offset */ - u16 dgl; /* Datagram label */ - u16 res3; -} __attribute__((packed)); -#elif defined __LITTLE_ENDIAN_BITFIELD -struct eth1394_sf_hdr { - u16 dg_size:12; - u16 res1:2; - u16 lf:2; - u16 fg_off:12; - u16 res2:4; - u16 dgl; - u16 res3; -} __attribute__((packed)); -#else -#error Unknown bit field type -#endif - -#if defined __BIG_ENDIAN_BITFIELD -struct eth1394_common_hdr { - u16 lf:2; - u16 pad1:14; -} __attribute__((packed)); -#elif defined __LITTLE_ENDIAN_BITFIELD -struct eth1394_common_hdr { - u16 pad1:14; - u16 lf:2; -} __attribute__((packed)); -#else -#error Unknown bit field type -#endif - -struct eth1394_hdr_words { - u16 word1; - u16 word2; - u16 word3; - u16 word4; -}; - -union eth1394_hdr { - struct eth1394_common_hdr common; - struct eth1394_uf_hdr uf; - struct eth1394_ff_hdr ff; - struct eth1394_sf_hdr sf; - struct eth1394_hdr_words words; -}; - -/* End of IP1394 headers */ - -/* Fragment types */ -#define ETH1394_HDR_LF_UF 0 /* unfragmented */ -#define ETH1394_HDR_LF_FF 1 /* first fragment */ -#define ETH1394_HDR_LF_LF 2 /* last fragment */ -#define ETH1394_HDR_LF_IF 3 /* interior fragment */ - -#define IP1394_HW_ADDR_LEN 16 /* As per RFC */ - -/* Our arp packet (ARPHRD_IEEE1394) */ -struct eth1394_arp { - u16 hw_type; /* 0x0018 */ - u16 proto_type; /* 0x0806 */ - u8 hw_addr_len; /* 16 */ - u8 ip_addr_len; /* 4 */ - u16 opcode; /* ARP Opcode */ - /* Above is exactly the same format as struct arphdr */ - - __be64 s_uniq_id; /* Sender's 64bit EUI */ - u8 max_rec; /* Sender's max packet size */ - u8 sspd; /* Sender's max speed */ - __be16 fifo_hi; /* hi 16bits of sender's FIFO addr */ - __be32 fifo_lo; /* lo 32bits of sender's FIFO addr */ - u32 sip; /* Sender's IP Address */ - u32 tip; /* IP Address of requested hw addr */ -}; - -/* Network timeout */ -#define ETHER1394_TIMEOUT 100000 - -/* This is our task struct. It's used for the packet complete callback. */ -struct packet_task { - struct sk_buff *skb; - int outstanding_pkts; - eth1394_tx_type tx_type; - int max_payload; - struct hpsb_packet *packet; - struct eth1394_priv *priv; - union eth1394_hdr hdr; - u64 addr; - u16 dest_node; -}; - -#endif /* __ETH1394_H */ diff --git a/drivers/ieee1394/highlevel.c b/drivers/ieee1394/highlevel.c deleted file mode 100644 index 4bc443546e04..000000000000 --- a/drivers/ieee1394/highlevel.c +++ /dev/null @@ -1,691 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * Copyright (C) 1999 Andreas E. Bombe - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - * - * - * Contributions: - * - * Christian Toegel - * unregister address space - * - * Manfred Weihs - * unregister address space - * - */ - -#include -#include -#include - -#include "ieee1394.h" -#include "ieee1394_types.h" -#include "hosts.h" -#include "ieee1394_core.h" -#include "highlevel.h" -#include "nodemgr.h" - - -struct hl_host_info { - struct list_head list; - struct hpsb_host *host; - size_t size; - unsigned long key; - void *data; -}; - - -static LIST_HEAD(hl_drivers); -static DECLARE_RWSEM(hl_drivers_sem); - -static LIST_HEAD(hl_irqs); -static DEFINE_RWLOCK(hl_irqs_lock); - -static DEFINE_RWLOCK(addr_space_lock); - - -static struct hl_host_info *hl_get_hostinfo(struct hpsb_highlevel *hl, - struct hpsb_host *host) -{ - struct hl_host_info *hi = NULL; - - if (!hl || !host) - return NULL; - - read_lock(&hl->host_info_lock); - list_for_each_entry(hi, &hl->host_info_list, list) { - if (hi->host == host) { - read_unlock(&hl->host_info_lock); - return hi; - } - } - read_unlock(&hl->host_info_lock); - return NULL; -} - -/** - * hpsb_get_hostinfo - retrieve a hostinfo pointer bound to this driver/host - * - * Returns a per @host and @hl driver data structure that was previously stored - * by hpsb_create_hostinfo. - */ -void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host) -{ - struct hl_host_info *hi = hl_get_hostinfo(hl, host); - - return hi ? hi->data : NULL; -} - -/** - * hpsb_create_hostinfo - allocate a hostinfo pointer bound to this driver/host - * - * Allocate a hostinfo pointer backed by memory with @data_size and bind it to - * to this @hl driver and @host. If @data_size is zero, then the return here is - * only valid for error checking. - */ -void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, - size_t data_size) -{ - struct hl_host_info *hi; - void *data; - unsigned long flags; - - hi = hl_get_hostinfo(hl, host); - if (hi) { - HPSB_ERR("%s called hpsb_create_hostinfo when hostinfo already" - " exists", hl->name); - return NULL; - } - - hi = kzalloc(sizeof(*hi) + data_size, GFP_ATOMIC); - if (!hi) - return NULL; - - if (data_size) { - data = hi->data = hi + 1; - hi->size = data_size; - } else - data = hi; - - hi->host = host; - - write_lock_irqsave(&hl->host_info_lock, flags); - list_add_tail(&hi->list, &hl->host_info_list); - write_unlock_irqrestore(&hl->host_info_lock, flags); - - return data; -} - -/** - * hpsb_set_hostinfo - set the hostinfo pointer to something useful - * - * Usually follows a call to hpsb_create_hostinfo, where the size is 0. - */ -int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, - void *data) -{ - struct hl_host_info *hi; - - hi = hl_get_hostinfo(hl, host); - if (hi) { - if (!hi->size && !hi->data) { - hi->data = data; - return 0; - } else - HPSB_ERR("%s called hpsb_set_hostinfo when hostinfo " - "already has data", hl->name); - } else - HPSB_ERR("%s called hpsb_set_hostinfo when no hostinfo exists", - hl->name); - return -EINVAL; -} - -/** - * hpsb_destroy_hostinfo - free and remove a hostinfo pointer - * - * Free and remove the hostinfo pointer bound to this @hl driver and @host. - */ -void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host) -{ - struct hl_host_info *hi; - - hi = hl_get_hostinfo(hl, host); - if (hi) { - unsigned long flags; - write_lock_irqsave(&hl->host_info_lock, flags); - list_del(&hi->list); - write_unlock_irqrestore(&hl->host_info_lock, flags); - kfree(hi); - } - return; -} - -/** - * hpsb_set_hostinfo_key - set an alternate lookup key for an hostinfo - * - * Sets an alternate lookup key for the hostinfo bound to this @hl driver and - * @host. - */ -void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, - unsigned long key) -{ - struct hl_host_info *hi; - - hi = hl_get_hostinfo(hl, host); - if (hi) - hi->key = key; - return; -} - -/** - * hpsb_get_hostinfo_bykey - retrieve a hostinfo pointer by its alternate key - */ -void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key) -{ - struct hl_host_info *hi; - void *data = NULL; - - if (!hl) - return NULL; - - read_lock(&hl->host_info_lock); - list_for_each_entry(hi, &hl->host_info_list, list) { - if (hi->key == key) { - data = hi->data; - break; - } - } - read_unlock(&hl->host_info_lock); - return data; -} - -static int highlevel_for_each_host_reg(struct hpsb_host *host, void *__data) -{ - struct hpsb_highlevel *hl = __data; - - hl->add_host(host); - - if (host->update_config_rom && hpsb_update_config_rom_image(host) < 0) - HPSB_ERR("Failed to generate Configuration ROM image for host " - "%s-%d", hl->name, host->id); - return 0; -} - -/** - * hpsb_register_highlevel - register highlevel driver - * - * The name pointer in @hl has to stay valid at all times because the string is - * not copied. - */ -void hpsb_register_highlevel(struct hpsb_highlevel *hl) -{ - unsigned long flags; - - hpsb_init_highlevel(hl); - INIT_LIST_HEAD(&hl->addr_list); - - down_write(&hl_drivers_sem); - list_add_tail(&hl->hl_list, &hl_drivers); - up_write(&hl_drivers_sem); - - write_lock_irqsave(&hl_irqs_lock, flags); - list_add_tail(&hl->irq_list, &hl_irqs); - write_unlock_irqrestore(&hl_irqs_lock, flags); - - if (hl->add_host) - nodemgr_for_each_host(hl, highlevel_for_each_host_reg); - return; -} - -static void __delete_addr(struct hpsb_address_serve *as) -{ - list_del(&as->host_list); - list_del(&as->hl_list); - kfree(as); -} - -static void __unregister_host(struct hpsb_highlevel *hl, struct hpsb_host *host, - int update_cr) -{ - unsigned long flags; - struct list_head *lh, *next; - struct hpsb_address_serve *as; - - /* First, let the highlevel driver unreg */ - if (hl->remove_host) - hl->remove_host(host); - - /* Remove any addresses that are matched for this highlevel driver - * and this particular host. */ - write_lock_irqsave(&addr_space_lock, flags); - list_for_each_safe (lh, next, &hl->addr_list) { - as = list_entry(lh, struct hpsb_address_serve, hl_list); - if (as->host == host) - __delete_addr(as); - } - write_unlock_irqrestore(&addr_space_lock, flags); - - /* Now update the config-rom to reflect anything removed by the - * highlevel driver. */ - if (update_cr && host->update_config_rom && - hpsb_update_config_rom_image(host) < 0) - HPSB_ERR("Failed to generate Configuration ROM image for host " - "%s-%d", hl->name, host->id); - - /* Finally remove all the host info associated between these two. */ - hpsb_destroy_hostinfo(hl, host); -} - -static int highlevel_for_each_host_unreg(struct hpsb_host *host, void *__data) -{ - struct hpsb_highlevel *hl = __data; - - __unregister_host(hl, host, 1); - return 0; -} - -/** - * hpsb_unregister_highlevel - unregister highlevel driver - */ -void hpsb_unregister_highlevel(struct hpsb_highlevel *hl) -{ - unsigned long flags; - - write_lock_irqsave(&hl_irqs_lock, flags); - list_del(&hl->irq_list); - write_unlock_irqrestore(&hl_irqs_lock, flags); - - down_write(&hl_drivers_sem); - list_del(&hl->hl_list); - up_write(&hl_drivers_sem); - - nodemgr_for_each_host(hl, highlevel_for_each_host_unreg); -} - -/** - * hpsb_allocate_and_register_addrspace - alloc' and reg' a host address space - * - * @start and @end are 48 bit pointers and have to be quadlet aligned. - * @end points to the first address behind the handled addresses. This - * function can be called multiple times for a single hpsb_highlevel @hl to - * implement sparse register sets. The requested region must not overlap any - * previously allocated region, otherwise registering will fail. - * - * It returns true for successful allocation. Address spaces can be - * unregistered with hpsb_unregister_addrspace. All remaining address spaces - * are automatically deallocated together with the hpsb_highlevel @hl. - */ -u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl, - struct hpsb_host *host, - const struct hpsb_address_ops *ops, - u64 size, u64 alignment, - u64 start, u64 end) -{ - struct hpsb_address_serve *as, *a1, *a2; - struct list_head *entry; - u64 retval = CSR1212_INVALID_ADDR_SPACE; - unsigned long flags; - u64 align_mask = ~(alignment - 1); - - if ((alignment & 3) || (alignment > 0x800000000000ULL) || - (hweight64(alignment) != 1)) { - HPSB_ERR("%s called with invalid alignment: 0x%048llx", - __func__, (unsigned long long)alignment); - return retval; - } - - /* default range, - * avoids controller's posted write area (see OHCI 1.1 clause 1.5) */ - if (start == CSR1212_INVALID_ADDR_SPACE && - end == CSR1212_INVALID_ADDR_SPACE) { - start = host->middle_addr_space; - end = CSR1212_ALL_SPACE_END; - } - - if (((start|end) & ~align_mask) || (start >= end) || - (end > CSR1212_ALL_SPACE_END)) { - HPSB_ERR("%s called with invalid addresses " - "(start = %012Lx end = %012Lx)", __func__, - (unsigned long long)start,(unsigned long long)end); - return retval; - } - - as = kmalloc(sizeof(*as), GFP_KERNEL); - if (!as) - return retval; - - INIT_LIST_HEAD(&as->host_list); - INIT_LIST_HEAD(&as->hl_list); - as->op = ops; - as->host = host; - - write_lock_irqsave(&addr_space_lock, flags); - list_for_each(entry, &host->addr_space) { - u64 a1sa, a1ea; - u64 a2sa, a2ea; - - a1 = list_entry(entry, struct hpsb_address_serve, host_list); - a2 = list_entry(entry->next, struct hpsb_address_serve, - host_list); - - a1sa = a1->start & align_mask; - a1ea = (a1->end + alignment -1) & align_mask; - a2sa = a2->start & align_mask; - a2ea = (a2->end + alignment -1) & align_mask; - - if ((a2sa - a1ea >= size) && (a2sa - start >= size) && - (a2sa > start)) { - as->start = max(start, a1ea); - as->end = as->start + size; - list_add(&as->host_list, entry); - list_add_tail(&as->hl_list, &hl->addr_list); - retval = as->start; - break; - } - } - write_unlock_irqrestore(&addr_space_lock, flags); - - if (retval == CSR1212_INVALID_ADDR_SPACE) - kfree(as); - return retval; -} - -/** - * hpsb_register_addrspace - register a host address space - * - * @start and @end are 48 bit pointers and have to be quadlet aligned. - * @end points to the first address behind the handled addresses. This - * function can be called multiple times for a single hpsb_highlevel @hl to - * implement sparse register sets. The requested region must not overlap any - * previously allocated region, otherwise registering will fail. - * - * It returns true for successful allocation. Address spaces can be - * unregistered with hpsb_unregister_addrspace. All remaining address spaces - * are automatically deallocated together with the hpsb_highlevel @hl. - */ -int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, - const struct hpsb_address_ops *ops, - u64 start, u64 end) -{ - struct hpsb_address_serve *as; - struct list_head *lh; - int retval = 0; - unsigned long flags; - - if (((start|end) & 3) || (start >= end) || - (end > CSR1212_ALL_SPACE_END)) { - HPSB_ERR("%s called with invalid addresses", __func__); - return 0; - } - - as = kmalloc(sizeof(*as), GFP_KERNEL); - if (!as) - return 0; - - INIT_LIST_HEAD(&as->host_list); - INIT_LIST_HEAD(&as->hl_list); - as->op = ops; - as->start = start; - as->end = end; - as->host = host; - - write_lock_irqsave(&addr_space_lock, flags); - list_for_each(lh, &host->addr_space) { - struct hpsb_address_serve *as_this = - list_entry(lh, struct hpsb_address_serve, host_list); - struct hpsb_address_serve *as_next = - list_entry(lh->next, struct hpsb_address_serve, - host_list); - - if (as_this->end > as->start) - break; - - if (as_next->start >= as->end) { - list_add(&as->host_list, lh); - list_add_tail(&as->hl_list, &hl->addr_list); - retval = 1; - break; - } - } - write_unlock_irqrestore(&addr_space_lock, flags); - - if (retval == 0) - kfree(as); - return retval; -} - -int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, - u64 start) -{ - int retval = 0; - struct hpsb_address_serve *as; - struct list_head *lh, *next; - unsigned long flags; - - write_lock_irqsave(&addr_space_lock, flags); - list_for_each_safe (lh, next, &hl->addr_list) { - as = list_entry(lh, struct hpsb_address_serve, hl_list); - if (as->start == start && as->host == host) { - __delete_addr(as); - retval = 1; - break; - } - } - write_unlock_irqrestore(&addr_space_lock, flags); - return retval; -} - -static const struct hpsb_address_ops dummy_ops; - -/* dummy address spaces as lower and upper bounds of the host's a.s. list */ -static void init_hpsb_highlevel(struct hpsb_host *host) -{ - INIT_LIST_HEAD(&host->dummy_zero_addr.host_list); - INIT_LIST_HEAD(&host->dummy_zero_addr.hl_list); - INIT_LIST_HEAD(&host->dummy_max_addr.host_list); - INIT_LIST_HEAD(&host->dummy_max_addr.hl_list); - - host->dummy_zero_addr.op = host->dummy_max_addr.op = &dummy_ops; - - host->dummy_zero_addr.start = host->dummy_zero_addr.end = 0; - host->dummy_max_addr.start = host->dummy_max_addr.end = ((u64) 1) << 48; - - list_add_tail(&host->dummy_zero_addr.host_list, &host->addr_space); - list_add_tail(&host->dummy_max_addr.host_list, &host->addr_space); -} - -void highlevel_add_host(struct hpsb_host *host) -{ - struct hpsb_highlevel *hl; - - init_hpsb_highlevel(host); - - down_read(&hl_drivers_sem); - list_for_each_entry(hl, &hl_drivers, hl_list) { - if (hl->add_host) - hl->add_host(host); - } - up_read(&hl_drivers_sem); - if (host->update_config_rom && hpsb_update_config_rom_image(host) < 0) - HPSB_ERR("Failed to generate Configuration ROM image for host " - "%s-%d", hl->name, host->id); -} - -void highlevel_remove_host(struct hpsb_host *host) -{ - struct hpsb_highlevel *hl; - - down_read(&hl_drivers_sem); - list_for_each_entry(hl, &hl_drivers, hl_list) - __unregister_host(hl, host, 0); - up_read(&hl_drivers_sem); -} - -void highlevel_host_reset(struct hpsb_host *host) -{ - unsigned long flags; - struct hpsb_highlevel *hl; - - read_lock_irqsave(&hl_irqs_lock, flags); - list_for_each_entry(hl, &hl_irqs, irq_list) { - if (hl->host_reset) - hl->host_reset(host); - } - read_unlock_irqrestore(&hl_irqs_lock, flags); -} - -void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction, - void *data, size_t length) -{ - unsigned long flags; - struct hpsb_highlevel *hl; - int cts = ((quadlet_t *)data)[0] >> 4; - - read_lock_irqsave(&hl_irqs_lock, flags); - list_for_each_entry(hl, &hl_irqs, irq_list) { - if (hl->fcp_request) - hl->fcp_request(host, nodeid, direction, cts, data, - length); - } - read_unlock_irqrestore(&hl_irqs_lock, flags); -} - -/* - * highlevel_read, highlevel_write, highlevel_lock, highlevel_lock64: - * - * These functions are called to handle transactions. They are called when a - * packet arrives. The flags argument contains the second word of the first - * header quadlet of the incoming packet (containing transaction label, retry - * code, transaction code and priority). These functions either return a - * response code or a negative number. In the first case a response will be - * generated. In the latter case, no response will be sent and the driver which - * handled the request will send the response itself. - */ -int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr, - unsigned int length, u16 flags) -{ - struct hpsb_address_serve *as; - unsigned int partlength; - int rcode = RCODE_ADDRESS_ERROR; - - read_lock(&addr_space_lock); - list_for_each_entry(as, &host->addr_space, host_list) { - if (as->start > addr) - break; - - if (as->end > addr) { - partlength = min(as->end - addr, (u64) length); - - if (as->op->read) - rcode = as->op->read(host, nodeid, data, - addr, partlength, flags); - else - rcode = RCODE_TYPE_ERROR; - - data += partlength; - length -= partlength; - addr += partlength; - - if ((rcode != RCODE_COMPLETE) || !length) - break; - } - } - read_unlock(&addr_space_lock); - - if (length && (rcode == RCODE_COMPLETE)) - rcode = RCODE_ADDRESS_ERROR; - return rcode; -} - -int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data, - u64 addr, unsigned int length, u16 flags) -{ - struct hpsb_address_serve *as; - unsigned int partlength; - int rcode = RCODE_ADDRESS_ERROR; - - read_lock(&addr_space_lock); - list_for_each_entry(as, &host->addr_space, host_list) { - if (as->start > addr) - break; - - if (as->end > addr) { - partlength = min(as->end - addr, (u64) length); - - if (as->op->write) - rcode = as->op->write(host, nodeid, destid, - data, addr, partlength, - flags); - else - rcode = RCODE_TYPE_ERROR; - - data += partlength; - length -= partlength; - addr += partlength; - - if ((rcode != RCODE_COMPLETE) || !length) - break; - } - } - read_unlock(&addr_space_lock); - - if (length && (rcode == RCODE_COMPLETE)) - rcode = RCODE_ADDRESS_ERROR; - return rcode; -} - -int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store, - u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, - u16 flags) -{ - struct hpsb_address_serve *as; - int rcode = RCODE_ADDRESS_ERROR; - - read_lock(&addr_space_lock); - list_for_each_entry(as, &host->addr_space, host_list) { - if (as->start > addr) - break; - - if (as->end > addr) { - if (as->op->lock) - rcode = as->op->lock(host, nodeid, store, addr, - data, arg, ext_tcode, - flags); - else - rcode = RCODE_TYPE_ERROR; - break; - } - } - read_unlock(&addr_space_lock); - return rcode; -} - -int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store, - u64 addr, octlet_t data, octlet_t arg, int ext_tcode, - u16 flags) -{ - struct hpsb_address_serve *as; - int rcode = RCODE_ADDRESS_ERROR; - - read_lock(&addr_space_lock); - - list_for_each_entry(as, &host->addr_space, host_list) { - if (as->start > addr) - break; - - if (as->end > addr) { - if (as->op->lock64) - rcode = as->op->lock64(host, nodeid, store, - addr, data, arg, - ext_tcode, flags); - else - rcode = RCODE_TYPE_ERROR; - break; - } - } - read_unlock(&addr_space_lock); - return rcode; -} diff --git a/drivers/ieee1394/highlevel.h b/drivers/ieee1394/highlevel.h deleted file mode 100644 index 9dba89fc60ad..000000000000 --- a/drivers/ieee1394/highlevel.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef IEEE1394_HIGHLEVEL_H -#define IEEE1394_HIGHLEVEL_H - -#include -#include -#include - -struct module; - -#include "ieee1394_types.h" - -struct hpsb_host; - -/* internal to ieee1394 core */ -struct hpsb_address_serve { - struct list_head host_list; /* per host list */ - struct list_head hl_list; /* hpsb_highlevel list */ - const struct hpsb_address_ops *op; - struct hpsb_host *host; - u64 start; /* first address handled, quadlet aligned */ - u64 end; /* first address behind, quadlet aligned */ -}; - -/* Only the following structures are of interest to actual highlevel drivers. */ - -struct hpsb_highlevel { - const char *name; - - /* Any of the following pointers can legally be NULL. */ - - /* New host initialized. Will also be called during - * hpsb_register_highlevel for all hosts already installed. */ - void (*add_host)(struct hpsb_host *host); - - /* Host about to be removed. Will also be called during - * hpsb_unregister_highlevel once for each host. */ - void (*remove_host)(struct hpsb_host *host); - - /* Host experienced bus reset with possible configuration changes. - * Note that this one may occur during interrupt/bottom half handling. - * You can not expect to be able to do stock hpsb_reads. */ - void (*host_reset)(struct hpsb_host *host); - - /* A write request was received on either the FCP_COMMAND (direction = - * 0) or the FCP_RESPONSE (direction = 1) register. The cts arg - * contains the cts field (first byte of data). */ - void (*fcp_request)(struct hpsb_host *host, int nodeid, int direction, - int cts, u8 *data, size_t length); - - /* These are initialized by the subsystem when the - * hpsb_higlevel is registered. */ - struct list_head hl_list; - struct list_head irq_list; - struct list_head addr_list; - - struct list_head host_info_list; - rwlock_t host_info_lock; -}; - -struct hpsb_address_ops { - /* - * Null function pointers will make the respective operation complete - * with RCODE_TYPE_ERROR. Makes for easy to implement read-only - * registers (just leave everything but read NULL). - * - * All functions shall return appropriate IEEE 1394 rcodes. - */ - - /* These functions have to implement block reads for themselves. - * - * These functions either return a response code or a negative number. - * In the first case a response will be generated. In the latter case, - * no response will be sent and the driver which handled the request - * will send the response itself. */ - int (*read)(struct hpsb_host *host, int nodeid, quadlet_t *buffer, - u64 addr, size_t length, u16 flags); - int (*write)(struct hpsb_host *host, int nodeid, int destid, - quadlet_t *data, u64 addr, size_t length, u16 flags); - - /* Lock transactions: write results of ext_tcode operation into - * *store. */ - int (*lock)(struct hpsb_host *host, int nodeid, quadlet_t *store, - u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, - u16 flags); - int (*lock64)(struct hpsb_host *host, int nodeid, octlet_t *store, - u64 addr, octlet_t data, octlet_t arg, int ext_tcode, - u16 flags); -}; - -void highlevel_add_host(struct hpsb_host *host); -void highlevel_remove_host(struct hpsb_host *host); -void highlevel_host_reset(struct hpsb_host *host); -int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr, - unsigned int length, u16 flags); -int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data, - u64 addr, unsigned int length, u16 flags); -int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store, - u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, - u16 flags); -int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store, - u64 addr, octlet_t data, octlet_t arg, int ext_tcode, - u16 flags); -void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction, - void *data, size_t length); - -/** - * hpsb_init_highlevel - initialize a struct hpsb_highlevel - * - * This is only necessary if hpsb_get_hostinfo_bykey can be called - * before hpsb_register_highlevel. - */ -static inline void hpsb_init_highlevel(struct hpsb_highlevel *hl) -{ - rwlock_init(&hl->host_info_lock); - INIT_LIST_HEAD(&hl->host_info_list); -} -void hpsb_register_highlevel(struct hpsb_highlevel *hl); -void hpsb_unregister_highlevel(struct hpsb_highlevel *hl); - -u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl, - struct hpsb_host *host, - const struct hpsb_address_ops *ops, - u64 size, u64 alignment, - u64 start, u64 end); -int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, - const struct hpsb_address_ops *ops, - u64 start, u64 end); -int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, - u64 start); - -void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); -void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, - size_t data_size); -void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); -void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, - unsigned long key); -void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key); -int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, - void *data); - -#endif /* IEEE1394_HIGHLEVEL_H */ diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c deleted file mode 100644 index e947d8ffac85..000000000000 --- a/drivers/ieee1394/hosts.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * Low level (host adapter) management. - * - * Copyright (C) 1999 Andreas E. Bombe - * Copyright (C) 1999 Emanuel Pirker - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "csr1212.h" -#include "ieee1394.h" -#include "ieee1394_types.h" -#include "hosts.h" -#include "ieee1394_core.h" -#include "highlevel.h" -#include "nodemgr.h" -#include "csr.h" -#include "config_roms.h" - - -static void delayed_reset_bus(struct work_struct *work) -{ - struct hpsb_host *host = - container_of(work, struct hpsb_host, delayed_reset.work); - u8 generation = host->csr.generation + 1; - - /* The generation field rolls over to 2 rather than 0 per IEEE - * 1394a-2000. */ - if (generation > 0xf || generation < 2) - generation = 2; - - csr_set_bus_info_generation(host->csr.rom, generation); - if (csr1212_generate_csr_image(host->csr.rom) != CSR1212_SUCCESS) { - /* CSR image creation failed. - * Reset generation field and do not issue a bus reset. */ - csr_set_bus_info_generation(host->csr.rom, - host->csr.generation); - return; - } - - host->csr.generation = generation; - - host->update_config_rom = 0; - if (host->driver->set_hw_config_rom) - host->driver->set_hw_config_rom(host, - host->csr.rom->bus_info_data); - - host->csr.gen_timestamp[host->csr.generation] = jiffies; - hpsb_reset_bus(host, SHORT_RESET); -} - -static int dummy_transmit_packet(struct hpsb_host *h, struct hpsb_packet *p) -{ - return 0; -} - -static int dummy_devctl(struct hpsb_host *h, enum devctl_cmd c, int arg) -{ - return -1; -} - -static int dummy_isoctl(struct hpsb_iso *iso, enum isoctl_cmd command, - unsigned long arg) -{ - return -1; -} - -static struct hpsb_host_driver dummy_driver = { - .transmit_packet = dummy_transmit_packet, - .devctl = dummy_devctl, - .isoctl = dummy_isoctl -}; - -static int alloc_hostnum_cb(struct hpsb_host *host, void *__data) -{ - int *hostnum = __data; - - if (host->id == *hostnum) - return 1; - - return 0; -} - -static DEFINE_MUTEX(host_num_alloc); - -/** - * hpsb_alloc_host - allocate a new host controller. - * @drv: the driver that will manage the host controller - * @extra: number of extra bytes to allocate for the driver - * - * Allocate a &hpsb_host and initialize the general subsystem specific - * fields. If the driver needs to store per host data, as drivers - * usually do, the amount of memory required can be specified by the - * @extra parameter. Once allocated, the driver should initialize the - * driver specific parts, enable the controller and make it available - * to the general subsystem using hpsb_add_host(). - * - * Return Value: a pointer to the &hpsb_host if successful, %NULL if - * no memory was available. - */ -struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra, - struct device *dev) -{ - struct hpsb_host *h; - int i; - int hostnum = 0; - - h = kzalloc(sizeof(*h) + extra, GFP_KERNEL); - if (!h) - return NULL; - - h->csr.rom = csr1212_create_csr(&csr_bus_ops, CSR_BUS_INFO_SIZE, h); - if (!h->csr.rom) - goto fail; - - h->hostdata = h + 1; - h->driver = drv; - - INIT_LIST_HEAD(&h->pending_packets); - INIT_LIST_HEAD(&h->addr_space); - - for (i = 2; i < 16; i++) - h->csr.gen_timestamp[i] = jiffies - 60 * HZ; - - atomic_set(&h->generation, 0); - - INIT_DELAYED_WORK(&h->delayed_reset, delayed_reset_bus); - - init_timer(&h->timeout); - h->timeout.data = (unsigned long) h; - h->timeout.function = abort_timedouts; - h->timeout_interval = HZ / 20; /* 50ms, half of minimum SPLIT_TIMEOUT */ - - h->topology_map = h->csr.topology_map + 3; - h->speed_map = (u8 *)(h->csr.speed_map + 2); - - mutex_lock(&host_num_alloc); - while (nodemgr_for_each_host(&hostnum, alloc_hostnum_cb)) - hostnum++; - mutex_unlock(&host_num_alloc); - h->id = hostnum; - - memcpy(&h->device, &nodemgr_dev_template_host, sizeof(h->device)); - h->device.parent = dev; - set_dev_node(&h->device, dev_to_node(dev)); - dev_set_name(&h->device, "fw-host%d", h->id); - - h->host_dev.parent = &h->device; - h->host_dev.class = &hpsb_host_class; - dev_set_name(&h->host_dev, "fw-host%d", h->id); - - if (device_register(&h->device)) - goto fail; - if (device_register(&h->host_dev)) { - device_unregister(&h->device); - goto fail; - } - get_device(&h->device); - - return h; - -fail: - kfree(h); - return NULL; -} - -int hpsb_add_host(struct hpsb_host *host) -{ - if (hpsb_default_host_entry(host)) - return -ENOMEM; - - highlevel_add_host(host); - return 0; -} - -void hpsb_resume_host(struct hpsb_host *host) -{ - if (host->driver->set_hw_config_rom) - host->driver->set_hw_config_rom(host, - host->csr.rom->bus_info_data); - host->driver->devctl(host, RESET_BUS, SHORT_RESET); -} - -void hpsb_remove_host(struct hpsb_host *host) -{ - host->is_shutdown = 1; - - cancel_delayed_work(&host->delayed_reset); - flush_scheduled_work(); - - host->driver = &dummy_driver; - highlevel_remove_host(host); - - device_unregister(&host->host_dev); - device_unregister(&host->device); -} - -/** - * hpsb_update_config_rom_image - updates configuration ROM image of a host - * - * Updates the configuration ROM image of a host. rom_version must be the - * current version, otherwise it will fail with return value -1. If this - * host does not support config-rom-update, it will return -%EINVAL. - * Return value 0 indicates success. - */ -int hpsb_update_config_rom_image(struct hpsb_host *host) -{ - unsigned long reset_delay; - int next_gen = host->csr.generation + 1; - - if (!host->update_config_rom) - return -EINVAL; - - if (next_gen > 0xf) - next_gen = 2; - - /* Stop the delayed interrupt, we're about to change the config rom and - * it would be a waste to do a bus reset twice. */ - cancel_delayed_work(&host->delayed_reset); - - /* IEEE 1394a-2000 prohibits using the same generation number - * twice in a 60 second period. */ - if (time_before(jiffies, host->csr.gen_timestamp[next_gen] + 60 * HZ)) - /* Wait 60 seconds from the last time this generation number was - * used. */ - reset_delay = - (60 * HZ) + host->csr.gen_timestamp[next_gen] - jiffies; - else - /* Wait 1 second in case some other code wants to change the - * Config ROM in the near future. */ - reset_delay = HZ; - - PREPARE_DELAYED_WORK(&host->delayed_reset, delayed_reset_bus); - schedule_delayed_work(&host->delayed_reset, reset_delay); - - return 0; -} diff --git a/drivers/ieee1394/hosts.h b/drivers/ieee1394/hosts.h deleted file mode 100644 index 49c359022c54..000000000000 --- a/drivers/ieee1394/hosts.h +++ /dev/null @@ -1,201 +0,0 @@ -#ifndef _IEEE1394_HOSTS_H -#define _IEEE1394_HOSTS_H - -#include -#include -#include -#include -#include -#include - -struct pci_dev; -struct module; - -#include "ieee1394_types.h" -#include "csr.h" -#include "highlevel.h" - -struct hpsb_packet; -struct hpsb_iso; - -struct hpsb_host { - struct list_head host_list; - - void *hostdata; - - atomic_t generation; - - struct list_head pending_packets; - struct timer_list timeout; - unsigned long timeout_interval; - - int node_count; /* number of identified nodes on this bus */ - int selfid_count; /* total number of SelfIDs received */ - int nodes_active; /* number of nodes with active link layer */ - - nodeid_t node_id; /* node ID of this host */ - nodeid_t irm_id; /* ID of this bus' isochronous resource manager */ - nodeid_t busmgr_id; /* ID of this bus' bus manager */ - - /* this nodes state */ - unsigned in_bus_reset:1; - unsigned is_shutdown:1; - unsigned resume_packet_sent:1; - - /* this nodes' duties on the bus */ - unsigned is_root:1; - unsigned is_cycmst:1; - unsigned is_irm:1; - unsigned is_busmgr:1; - - int reset_retries; - quadlet_t *topology_map; - u8 *speed_map; - - int id; - struct hpsb_host_driver *driver; - struct pci_dev *pdev; - struct device device; - struct device host_dev; - - struct delayed_work delayed_reset; - unsigned config_roms:31; - unsigned update_config_rom:1; - - struct list_head addr_space; - u64 low_addr_space; /* upper bound of physical DMA area */ - u64 middle_addr_space; /* upper bound of posted write area */ - - u8 speed[ALL_NODES]; /* speed between each node and local node */ - - /* per node tlabel allocation */ - u8 next_tl[ALL_NODES]; - struct { DECLARE_BITMAP(map, 64); } tl_pool[ALL_NODES]; - - struct csr_control csr; - - struct hpsb_address_serve dummy_zero_addr; - struct hpsb_address_serve dummy_max_addr; -}; - -enum devctl_cmd { - /* Host is requested to reset its bus and cancel all outstanding async - * requests. If arg == 1, it shall also attempt to become root on the - * bus. Return void. */ - RESET_BUS, - - /* Arg is void, return value is the hardware cycle counter value. */ - GET_CYCLE_COUNTER, - - /* Set the hardware cycle counter to the value in arg, return void. - * FIXME - setting is probably not required. */ - SET_CYCLE_COUNTER, - - /* Configure hardware for new bus ID in arg, return void. */ - SET_BUS_ID, - - /* If arg true, start sending cycle start packets, stop if arg == 0. - * Return void. */ - ACT_CYCLE_MASTER, - - /* Cancel all outstanding async requests without resetting the bus. - * Return void. */ - CANCEL_REQUESTS, -}; - -enum isoctl_cmd { - /* rawiso API - see iso.h for the meanings of these commands - * (they correspond exactly to the hpsb_iso_* API functions) - * INIT = allocate resources - * START = begin transmission/reception - * STOP = halt transmission/reception - * QUEUE/RELEASE = produce/consume packets - * SHUTDOWN = deallocate resources - */ - - XMIT_INIT, - XMIT_START, - XMIT_STOP, - XMIT_QUEUE, - XMIT_SHUTDOWN, - - RECV_INIT, - RECV_LISTEN_CHANNEL, /* multi-channel only */ - RECV_UNLISTEN_CHANNEL, /* multi-channel only */ - RECV_SET_CHANNEL_MASK, /* multi-channel only; arg is a *u64 */ - RECV_START, - RECV_STOP, - RECV_RELEASE, - RECV_SHUTDOWN, - RECV_FLUSH -}; - -enum reset_types { - /* 166 microsecond reset -- only type of reset available on - non-1394a capable controllers */ - LONG_RESET, - - /* Short (arbitrated) reset -- only available on 1394a capable - controllers */ - SHORT_RESET, - - /* Variants that set force_root before issueing the bus reset */ - LONG_RESET_FORCE_ROOT, SHORT_RESET_FORCE_ROOT, - - /* Variants that clear force_root before issueing the bus reset */ - LONG_RESET_NO_FORCE_ROOT, SHORT_RESET_NO_FORCE_ROOT -}; - -struct hpsb_host_driver { - struct module *owner; - const char *name; - - /* The hardware driver may optionally support a function that is used - * to set the hardware ConfigROM if the hardware supports handling - * reads to the ConfigROM on its own. */ - void (*set_hw_config_rom)(struct hpsb_host *host, - __be32 *config_rom); - - /* This function shall implement packet transmission based on - * packet->type. It shall CRC both parts of the packet (unless - * packet->type == raw) and do byte-swapping as necessary or instruct - * the hardware to do so. It can return immediately after the packet - * was queued for sending. After sending, hpsb_sent_packet() has to be - * called. Return 0 on success, negative errno on failure. - * NOTE: The function must be callable in interrupt context. - */ - int (*transmit_packet)(struct hpsb_host *host, - struct hpsb_packet *packet); - - /* This function requests miscellanous services from the driver, see - * above for command codes and expected actions. Return -1 for unknown - * command, though that should never happen. - */ - int (*devctl)(struct hpsb_host *host, enum devctl_cmd command, int arg); - - /* ISO transmission/reception functions. Return 0 on success, -1 - * (or -EXXX errno code) on failure. If the low-level driver does not - * support the new ISO API, set isoctl to NULL. - */ - int (*isoctl)(struct hpsb_iso *iso, enum isoctl_cmd command, - unsigned long arg); - - /* This function is mainly to redirect local CSR reads/locks to the iso - * management registers (bus manager id, bandwidth available, channels - * available) to the hardware registers in OHCI. reg is 0,1,2,3 for bus - * mgr, bwdth avail, ch avail hi, ch avail lo respectively (the same ids - * as OHCI uses). data and compare are the new data and expected data - * respectively, return value is the old value. - */ - quadlet_t (*hw_csr_reg) (struct hpsb_host *host, int reg, - quadlet_t data, quadlet_t compare); -}; - -struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra, - struct device *dev); -int hpsb_add_host(struct hpsb_host *host); -void hpsb_resume_host(struct hpsb_host *host); -void hpsb_remove_host(struct hpsb_host *host); -int hpsb_update_config_rom_image(struct hpsb_host *host); - -#endif /* _IEEE1394_HOSTS_H */ diff --git a/drivers/ieee1394/ieee1394-ioctl.h b/drivers/ieee1394/ieee1394-ioctl.h deleted file mode 100644 index 46878fef136c..000000000000 --- a/drivers/ieee1394/ieee1394-ioctl.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Base file for all ieee1394 ioctl's. - * Linux-1394 has allocated base '#' with a range of 0x00-0x3f. - */ - -#ifndef __IEEE1394_IOCTL_H -#define __IEEE1394_IOCTL_H - -#include -#include - -/* DV1394 Gets 10 */ - -/* Get the driver ready to transmit video. pass a struct dv1394_init* as - * the parameter (see below), or NULL to get default parameters */ -#define DV1394_IOC_INIT _IOW('#', 0x06, struct dv1394_init) - -/* Stop transmitting video and free the ringbuffer */ -#define DV1394_IOC_SHUTDOWN _IO ('#', 0x07) - -/* Submit N new frames to be transmitted, where the index of the first new - * frame is first_clear_buffer, and the index of the last new frame is - * (first_clear_buffer + N) % n_frames */ -#define DV1394_IOC_SUBMIT_FRAMES _IO ('#', 0x08) - -/* Block until N buffers are clear (pass N as the parameter) Because we - * re-transmit the last frame on underrun, there will at most be n_frames - * - 1 clear frames at any time */ -#define DV1394_IOC_WAIT_FRAMES _IO ('#', 0x09) - -/* Capture new frames that have been received, where the index of the - * first new frame is first_clear_buffer, and the index of the last new - * frame is (first_clear_buffer + N) % n_frames */ -#define DV1394_IOC_RECEIVE_FRAMES _IO ('#', 0x0a) - -/* Tell card to start receiving DMA */ -#define DV1394_IOC_START_RECEIVE _IO ('#', 0x0b) - -/* Pass a struct dv1394_status* as the parameter */ -#define DV1394_IOC_GET_STATUS _IOR('#', 0x0c, struct dv1394_status) - - -/* Video1394 Gets 10 */ - -#define VIDEO1394_IOC_LISTEN_CHANNEL \ - _IOWR('#', 0x10, struct video1394_mmap) -#define VIDEO1394_IOC_UNLISTEN_CHANNEL \ - _IOW ('#', 0x11, int) -#define VIDEO1394_IOC_LISTEN_QUEUE_BUFFER \ - _IOW ('#', 0x12, struct video1394_wait) -#define VIDEO1394_IOC_LISTEN_WAIT_BUFFER \ - _IOWR('#', 0x13, struct video1394_wait) -#define VIDEO1394_IOC_TALK_CHANNEL \ - _IOWR('#', 0x14, struct video1394_mmap) -#define VIDEO1394_IOC_UNTALK_CHANNEL \ - _IOW ('#', 0x15, int) -/* - * This one is broken: it really wanted - * "sizeof (struct video1394_wait) + sizeof (struct video1394_queue_variable)" - * but got just a "size_t" - */ -#define VIDEO1394_IOC_TALK_QUEUE_BUFFER \ - _IOW ('#', 0x16, size_t) -#define VIDEO1394_IOC_TALK_WAIT_BUFFER \ - _IOW ('#', 0x17, struct video1394_wait) -#define VIDEO1394_IOC_LISTEN_POLL_BUFFER \ - _IOWR('#', 0x18, struct video1394_wait) - - -/* Raw1394's ISO interface */ -#define RAW1394_IOC_ISO_XMIT_INIT \ - _IOW ('#', 0x1a, struct raw1394_iso_status) -#define RAW1394_IOC_ISO_RECV_INIT \ - _IOWR('#', 0x1b, struct raw1394_iso_status) -#define RAW1394_IOC_ISO_RECV_START \ - _IOC (_IOC_WRITE, '#', 0x1c, sizeof(int) * 3) -#define RAW1394_IOC_ISO_XMIT_START \ - _IOC (_IOC_WRITE, '#', 0x1d, sizeof(int) * 2) -#define RAW1394_IOC_ISO_XMIT_RECV_STOP \ - _IO ('#', 0x1e) -#define RAW1394_IOC_ISO_GET_STATUS \ - _IOR ('#', 0x1f, struct raw1394_iso_status) -#define RAW1394_IOC_ISO_SHUTDOWN \ - _IO ('#', 0x20) -#define RAW1394_IOC_ISO_QUEUE_ACTIVITY \ - _IO ('#', 0x21) -#define RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL \ - _IOW ('#', 0x22, unsigned char) -#define RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL \ - _IOW ('#', 0x23, unsigned char) -#define RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK \ - _IOW ('#', 0x24, __u64) -#define RAW1394_IOC_ISO_RECV_PACKETS \ - _IOW ('#', 0x25, struct raw1394_iso_packets) -#define RAW1394_IOC_ISO_RECV_RELEASE_PACKETS \ - _IOW ('#', 0x26, unsigned int) -#define RAW1394_IOC_ISO_XMIT_PACKETS \ - _IOW ('#', 0x27, struct raw1394_iso_packets) -#define RAW1394_IOC_ISO_XMIT_SYNC \ - _IO ('#', 0x28) -#define RAW1394_IOC_ISO_RECV_FLUSH \ - _IO ('#', 0x29) -#define RAW1394_IOC_GET_CYCLE_TIMER \ - _IOR ('#', 0x30, struct raw1394_cycle_timer) - -#endif /* __IEEE1394_IOCTL_H */ diff --git a/drivers/ieee1394/ieee1394.h b/drivers/ieee1394/ieee1394.h deleted file mode 100644 index af320e2c5079..000000000000 --- a/drivers/ieee1394/ieee1394.h +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Generic IEEE 1394 definitions - */ - -#ifndef _IEEE1394_IEEE1394_H -#define _IEEE1394_IEEE1394_H - -#define TCODE_WRITEQ 0x0 -#define TCODE_WRITEB 0x1 -#define TCODE_WRITE_RESPONSE 0x2 -#define TCODE_READQ 0x4 -#define TCODE_READB 0x5 -#define TCODE_READQ_RESPONSE 0x6 -#define TCODE_READB_RESPONSE 0x7 -#define TCODE_CYCLE_START 0x8 -#define TCODE_LOCK_REQUEST 0x9 -#define TCODE_ISO_DATA 0xa -#define TCODE_STREAM_DATA 0xa -#define TCODE_LOCK_RESPONSE 0xb - -#define RCODE_COMPLETE 0x0 -#define RCODE_CONFLICT_ERROR 0x4 -#define RCODE_DATA_ERROR 0x5 -#define RCODE_TYPE_ERROR 0x6 -#define RCODE_ADDRESS_ERROR 0x7 - -#define EXTCODE_MASK_SWAP 0x1 -#define EXTCODE_COMPARE_SWAP 0x2 -#define EXTCODE_FETCH_ADD 0x3 -#define EXTCODE_LITTLE_ADD 0x4 -#define EXTCODE_BOUNDED_ADD 0x5 -#define EXTCODE_WRAP_ADD 0x6 - -#define ACK_COMPLETE 0x1 -#define ACK_PENDING 0x2 -#define ACK_BUSY_X 0x4 -#define ACK_BUSY_A 0x5 -#define ACK_BUSY_B 0x6 -#define ACK_TARDY 0xb -#define ACK_CONFLICT_ERROR 0xc -#define ACK_DATA_ERROR 0xd -#define ACK_TYPE_ERROR 0xe -#define ACK_ADDRESS_ERROR 0xf - -/* Non-standard "ACK codes" for internal use */ -#define ACKX_NONE (-1) -#define ACKX_SEND_ERROR (-2) -#define ACKX_ABORTED (-3) -#define ACKX_TIMEOUT (-4) - -#define IEEE1394_SPEED_100 0x00 -#define IEEE1394_SPEED_200 0x01 -#define IEEE1394_SPEED_400 0x02 -#define IEEE1394_SPEED_800 0x03 -#define IEEE1394_SPEED_1600 0x04 -#define IEEE1394_SPEED_3200 0x05 -#define IEEE1394_SPEED_MAX IEEE1394_SPEED_3200 - -/* Maps speed values above to a string representation */ -extern const char *hpsb_speedto_str[]; - -/* 1394a cable PHY packets */ -#define SELFID_PWRCL_NO_POWER 0x0 -#define SELFID_PWRCL_PROVIDE_15W 0x1 -#define SELFID_PWRCL_PROVIDE_30W 0x2 -#define SELFID_PWRCL_PROVIDE_45W 0x3 -#define SELFID_PWRCL_USE_1W 0x4 -#define SELFID_PWRCL_USE_3W 0x5 -#define SELFID_PWRCL_USE_6W 0x6 -#define SELFID_PWRCL_USE_10W 0x7 - -#define SELFID_PORT_CHILD 0x3 -#define SELFID_PORT_PARENT 0x2 -#define SELFID_PORT_NCONN 0x1 -#define SELFID_PORT_NONE 0x0 - -#define SELFID_SPEED_UNKNOWN 0x3 /* 1394b PHY */ - -#define PHYPACKET_LINKON 0x40000000 -#define PHYPACKET_PHYCONFIG_R 0x00800000 -#define PHYPACKET_PHYCONFIG_T 0x00400000 -#define EXTPHYPACKET_TYPE_PING 0x00000000 -#define EXTPHYPACKET_TYPE_REMOTEACCESS_BASE 0x00040000 -#define EXTPHYPACKET_TYPE_REMOTEACCESS_PAGED 0x00140000 -#define EXTPHYPACKET_TYPE_REMOTEREPLY_BASE 0x000C0000 -#define EXTPHYPACKET_TYPE_REMOTEREPLY_PAGED 0x001C0000 -#define EXTPHYPACKET_TYPE_REMOTECOMMAND 0x00200000 -#define EXTPHYPACKET_TYPE_REMOTECONFIRMATION 0x00280000 -#define EXTPHYPACKET_TYPE_RESUME 0x003C0000 - -#define EXTPHYPACKET_TYPEMASK 0xC0FC0000 - -#define PHYPACKET_PORT_SHIFT 24 -#define PHYPACKET_GAPCOUNT_SHIFT 16 - -/* 1394a PHY register map bitmasks */ -#define PHY_00_PHYSICAL_ID 0xFC -#define PHY_00_R 0x02 /* Root */ -#define PHY_00_PS 0x01 /* Power Status*/ -#define PHY_01_RHB 0x80 /* Root Hold-Off */ -#define PHY_01_IBR 0x80 /* Initiate Bus Reset */ -#define PHY_01_GAP_COUNT 0x3F -#define PHY_02_EXTENDED 0xE0 /* 0x7 for 1394a-compliant PHY */ -#define PHY_02_TOTAL_PORTS 0x1F -#define PHY_03_MAX_SPEED 0xE0 -#define PHY_03_DELAY 0x0F -#define PHY_04_LCTRL 0x80 /* Link Active Report Control */ -#define PHY_04_CONTENDER 0x40 -#define PHY_04_JITTER 0x38 -#define PHY_04_PWR_CLASS 0x07 /* Power Class */ -#define PHY_05_WATCHDOG 0x80 -#define PHY_05_ISBR 0x40 /* Initiate Short Bus Reset */ -#define PHY_05_LOOP 0x20 /* Loop Detect */ -#define PHY_05_PWR_FAIL 0x10 /* Cable Power Failure Detect */ -#define PHY_05_TIMEOUT 0x08 /* Arbitration State Machine Timeout */ -#define PHY_05_PORT_EVENT 0x04 /* Port Event Detect */ -#define PHY_05_ENAB_ACCEL 0x02 /* Enable Arbitration Acceleration */ -#define PHY_05_ENAB_MULTI 0x01 /* Ena. Multispeed Packet Concatenation */ - -#include - -/* '1' '3' '9' '4' in ASCII */ -#define IEEE1394_BUSID_MAGIC cpu_to_be32(0x31333934) - -#ifdef __BIG_ENDIAN_BITFIELD - -struct selfid { - u32 packet_identifier:2; /* always binary 10 */ - u32 phy_id:6; - /* byte */ - u32 extended:1; /* if true is struct ext_selfid */ - u32 link_active:1; - u32 gap_count:6; - /* byte */ - u32 speed:2; - u32 phy_delay:2; - u32 contender:1; - u32 power_class:3; - /* byte */ - u32 port0:2; - u32 port1:2; - u32 port2:2; - u32 initiated_reset:1; - u32 more_packets:1; -} __attribute__((packed)); - -struct ext_selfid { - u32 packet_identifier:2; /* always binary 10 */ - u32 phy_id:6; - /* byte */ - u32 extended:1; /* if false is struct selfid */ - u32 seq_nr:3; - u32 reserved:2; - u32 porta:2; - /* byte */ - u32 portb:2; - u32 portc:2; - u32 portd:2; - u32 porte:2; - /* byte */ - u32 portf:2; - u32 portg:2; - u32 porth:2; - u32 reserved2:1; - u32 more_packets:1; -} __attribute__((packed)); - -#elif defined __LITTLE_ENDIAN_BITFIELD /* __BIG_ENDIAN_BITFIELD */ - -/* - * Note: these mean to be bit fields of a big endian SelfID as seen on a little - * endian machine. Without swapping. - */ - -struct selfid { - u32 phy_id:6; - u32 packet_identifier:2; /* always binary 10 */ - /* byte */ - u32 gap_count:6; - u32 link_active:1; - u32 extended:1; /* if true is struct ext_selfid */ - /* byte */ - u32 power_class:3; - u32 contender:1; - u32 phy_delay:2; - u32 speed:2; - /* byte */ - u32 more_packets:1; - u32 initiated_reset:1; - u32 port2:2; - u32 port1:2; - u32 port0:2; -} __attribute__((packed)); - -struct ext_selfid { - u32 phy_id:6; - u32 packet_identifier:2; /* always binary 10 */ - /* byte */ - u32 porta:2; - u32 reserved:2; - u32 seq_nr:3; - u32 extended:1; /* if false is struct selfid */ - /* byte */ - u32 porte:2; - u32 portd:2; - u32 portc:2; - u32 portb:2; - /* byte */ - u32 more_packets:1; - u32 reserved2:1; - u32 porth:2; - u32 portg:2; - u32 portf:2; -} __attribute__((packed)); - -#else -#error What? PDP endian? -#endif /* __BIG_ENDIAN_BITFIELD */ - -#endif /* _IEEE1394_IEEE1394_H */ diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c deleted file mode 100644 index 872338003721..000000000000 --- a/drivers/ieee1394/ieee1394_core.c +++ /dev/null @@ -1,1380 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * Core support: hpsb_packet management, packet handling and forwarding to - * highlevel or lowlevel code - * - * Copyright (C) 1999, 2000 Andreas E. Bombe - * 2002 Manfred Weihs - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - * - * - * Contributions: - * - * Manfred Weihs - * loopback functionality in hpsb_send_packet - * allow highlevel drivers to disable automatic response generation - * and to generate responses themselves (deferred) - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "ieee1394_types.h" -#include "ieee1394.h" -#include "hosts.h" -#include "ieee1394_core.h" -#include "highlevel.h" -#include "ieee1394_transactions.h" -#include "csr.h" -#include "nodemgr.h" -#include "dma.h" -#include "iso.h" -#include "config_roms.h" - -/* - * Disable the nodemgr detection and config rom reading functionality. - */ -static int disable_nodemgr; -module_param(disable_nodemgr, int, 0444); -MODULE_PARM_DESC(disable_nodemgr, "Disable nodemgr functionality."); - -/* Disable Isochronous Resource Manager functionality */ -int hpsb_disable_irm = 0; -module_param_named(disable_irm, hpsb_disable_irm, bool, 0444); -MODULE_PARM_DESC(disable_irm, - "Disable Isochronous Resource Manager functionality."); - -/* We are GPL, so treat us special */ -MODULE_LICENSE("GPL"); - -/* Some globals used */ -const char *hpsb_speedto_str[] = { "S100", "S200", "S400", "S800", "S1600", "S3200" }; -struct class *hpsb_protocol_class; - -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG -static void dump_packet(const char *text, quadlet_t *data, int size, int speed) -{ - int i; - - size /= 4; - size = (size > 4 ? 4 : size); - - printk(KERN_DEBUG "ieee1394: %s", text); - if (speed > -1 && speed < 6) - printk(" at %s", hpsb_speedto_str[speed]); - printk(":"); - for (i = 0; i < size; i++) - printk(" %08x", data[i]); - printk("\n"); -} -#else -#define dump_packet(a,b,c,d) do {} while (0) -#endif - -static void abort_requests(struct hpsb_host *host); -static void queue_packet_complete(struct hpsb_packet *packet); - - -/** - * hpsb_set_packet_complete_task - set task that runs when a packet completes - * @packet: the packet whose completion we want the task added to - * @routine: function to call - * @data: data (if any) to pass to the above function - * - * Set the task that runs when a packet completes. You cannot call this more - * than once on a single packet before it is sent. - * - * Typically, the complete @routine is responsible to call hpsb_free_packet(). - */ -void hpsb_set_packet_complete_task(struct hpsb_packet *packet, - void (*routine)(void *), void *data) -{ - WARN_ON(packet->complete_routine != NULL); - packet->complete_routine = routine; - packet->complete_data = data; - return; -} - -/** - * hpsb_alloc_packet - allocate new packet structure - * @data_size: size of the data block to be allocated, in bytes - * - * This function allocates, initializes and returns a new &struct hpsb_packet. - * It can be used in interrupt context. A header block is always included and - * initialized with zeros. Its size is big enough to contain all possible 1394 - * headers. The data block is only allocated if @data_size is not zero. - * - * For packets for which responses will be received the @data_size has to be big - * enough to contain the response's data block since no further allocation - * occurs at response matching time. - * - * The packet's generation value will be set to the current generation number - * for ease of use. Remember to overwrite it with your own recorded generation - * number if you can not be sure that your code will not race with a bus reset. - * - * Return value: A pointer to a &struct hpsb_packet or NULL on allocation - * failure. - */ -struct hpsb_packet *hpsb_alloc_packet(size_t data_size) -{ - struct hpsb_packet *packet; - - data_size = ((data_size + 3) & ~3); - - packet = kzalloc(sizeof(*packet) + data_size, GFP_ATOMIC); - if (!packet) - return NULL; - - packet->state = hpsb_unused; - packet->generation = -1; - INIT_LIST_HEAD(&packet->driver_list); - INIT_LIST_HEAD(&packet->queue); - atomic_set(&packet->refcnt, 1); - - if (data_size) { - packet->data = packet->embedded_data; - packet->allocated_data_size = data_size; - } - return packet; -} - -/** - * hpsb_free_packet - free packet and data associated with it - * @packet: packet to free (is NULL safe) - * - * Frees @packet->data only if it was allocated through hpsb_alloc_packet(). - */ -void hpsb_free_packet(struct hpsb_packet *packet) -{ - if (packet && atomic_dec_and_test(&packet->refcnt)) { - BUG_ON(!list_empty(&packet->driver_list) || - !list_empty(&packet->queue)); - kfree(packet); - } -} - -/** - * hpsb_reset_bus - initiate bus reset on the given host - * @host: host controller whose bus to reset - * @type: one of enum reset_types - * - * Returns 1 if bus reset already in progress, 0 otherwise. - */ -int hpsb_reset_bus(struct hpsb_host *host, int type) -{ - if (!host->in_bus_reset) { - host->driver->devctl(host, RESET_BUS, type); - return 0; - } else { - return 1; - } -} - -/** - * hpsb_read_cycle_timer - read cycle timer register and system time - * @host: host whose isochronous cycle timer register is read - * @cycle_timer: address of bitfield to return the register contents - * @local_time: address to return the system time - * - * The format of * @cycle_timer, is described in OHCI 1.1 clause 5.13. This - * format is also read from non-OHCI controllers. * @local_time contains the - * system time in microseconds since the Epoch, read at the moment when the - * cycle timer was read. - * - * Return value: 0 for success or error number otherwise. - */ -int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer, - u64 *local_time) -{ - int ctr; - struct timeval tv; - unsigned long flags; - - if (!host || !cycle_timer || !local_time) - return -EINVAL; - - preempt_disable(); - local_irq_save(flags); - - ctr = host->driver->devctl(host, GET_CYCLE_COUNTER, 0); - if (ctr) - do_gettimeofday(&tv); - - local_irq_restore(flags); - preempt_enable(); - - if (!ctr) - return -EIO; - *cycle_timer = ctr; - *local_time = tv.tv_sec * 1000000ULL + tv.tv_usec; - return 0; -} - -/** - * hpsb_bus_reset - notify a bus reset to the core - * - * For host driver module usage. Safe to use in interrupt context, although - * quite complex; so you may want to run it in the bottom rather than top half. - * - * Returns 1 if bus reset already in progress, 0 otherwise. - */ -int hpsb_bus_reset(struct hpsb_host *host) -{ - if (host->in_bus_reset) { - HPSB_NOTICE("%s called while bus reset already in progress", - __func__); - return 1; - } - - abort_requests(host); - host->in_bus_reset = 1; - host->irm_id = -1; - host->is_irm = 0; - host->busmgr_id = -1; - host->is_busmgr = 0; - host->is_cycmst = 0; - host->node_count = 0; - host->selfid_count = 0; - - return 0; -} - - -/* - * Verify num_of_selfids SelfIDs and return number of nodes. Return zero in - * case verification failed. - */ -static int check_selfids(struct hpsb_host *host) -{ - int nodeid = -1; - int rest_of_selfids = host->selfid_count; - struct selfid *sid = (struct selfid *)host->topology_map; - struct ext_selfid *esid; - int esid_seq = 23; - - host->nodes_active = 0; - - while (rest_of_selfids--) { - if (!sid->extended) { - nodeid++; - esid_seq = 0; - - if (sid->phy_id != nodeid) { - HPSB_INFO("SelfIDs failed monotony check with " - "%d", sid->phy_id); - return 0; - } - - if (sid->link_active) { - host->nodes_active++; - if (sid->contender) - host->irm_id = LOCAL_BUS | sid->phy_id; - } - } else { - esid = (struct ext_selfid *)sid; - - if ((esid->phy_id != nodeid) - || (esid->seq_nr != esid_seq)) { - HPSB_INFO("SelfIDs failed monotony check with " - "%d/%d", esid->phy_id, esid->seq_nr); - return 0; - } - esid_seq++; - } - sid++; - } - - esid = (struct ext_selfid *)(sid - 1); - while (esid->extended) { - if ((esid->porta == SELFID_PORT_PARENT) || - (esid->portb == SELFID_PORT_PARENT) || - (esid->portc == SELFID_PORT_PARENT) || - (esid->portd == SELFID_PORT_PARENT) || - (esid->porte == SELFID_PORT_PARENT) || - (esid->portf == SELFID_PORT_PARENT) || - (esid->portg == SELFID_PORT_PARENT) || - (esid->porth == SELFID_PORT_PARENT)) { - HPSB_INFO("SelfIDs failed root check on " - "extended SelfID"); - return 0; - } - esid--; - } - - sid = (struct selfid *)esid; - if ((sid->port0 == SELFID_PORT_PARENT) || - (sid->port1 == SELFID_PORT_PARENT) || - (sid->port2 == SELFID_PORT_PARENT)) { - HPSB_INFO("SelfIDs failed root check"); - return 0; - } - - host->node_count = nodeid + 1; - return 1; -} - -static void build_speed_map(struct hpsb_host *host, int nodecount) -{ - u8 cldcnt[nodecount]; - u8 *map = host->speed_map; - u8 *speedcap = host->speed; - u8 local_link_speed = host->csr.lnk_spd; - struct selfid *sid; - struct ext_selfid *esid; - int i, j, n; - - for (i = 0; i < (nodecount * 64); i += 64) { - for (j = 0; j < nodecount; j++) { - map[i+j] = IEEE1394_SPEED_MAX; - } - } - - for (i = 0; i < nodecount; i++) { - cldcnt[i] = 0; - } - - /* find direct children count and speed */ - for (sid = (struct selfid *)&host->topology_map[host->selfid_count-1], - n = nodecount - 1; - (void *)sid >= (void *)host->topology_map; sid--) { - if (sid->extended) { - esid = (struct ext_selfid *)sid; - - if (esid->porta == SELFID_PORT_CHILD) cldcnt[n]++; - if (esid->portb == SELFID_PORT_CHILD) cldcnt[n]++; - if (esid->portc == SELFID_PORT_CHILD) cldcnt[n]++; - if (esid->portd == SELFID_PORT_CHILD) cldcnt[n]++; - if (esid->porte == SELFID_PORT_CHILD) cldcnt[n]++; - if (esid->portf == SELFID_PORT_CHILD) cldcnt[n]++; - if (esid->portg == SELFID_PORT_CHILD) cldcnt[n]++; - if (esid->porth == SELFID_PORT_CHILD) cldcnt[n]++; - } else { - if (sid->port0 == SELFID_PORT_CHILD) cldcnt[n]++; - if (sid->port1 == SELFID_PORT_CHILD) cldcnt[n]++; - if (sid->port2 == SELFID_PORT_CHILD) cldcnt[n]++; - - speedcap[n] = sid->speed; - if (speedcap[n] > local_link_speed) - speedcap[n] = local_link_speed; - n--; - } - } - - /* set self mapping */ - for (i = 0; i < nodecount; i++) { - map[64*i + i] = speedcap[i]; - } - - /* fix up direct children count to total children count; - * also fix up speedcaps for sibling and parent communication */ - for (i = 1; i < nodecount; i++) { - for (j = cldcnt[i], n = i - 1; j > 0; j--) { - cldcnt[i] += cldcnt[n]; - speedcap[n] = min(speedcap[n], speedcap[i]); - n -= cldcnt[n] + 1; - } - } - - for (n = 0; n < nodecount; n++) { - for (i = n - cldcnt[n]; i <= n; i++) { - for (j = 0; j < (n - cldcnt[n]); j++) { - map[j*64 + i] = map[i*64 + j] = - min(map[i*64 + j], speedcap[n]); - } - for (j = n + 1; j < nodecount; j++) { - map[j*64 + i] = map[i*64 + j] = - min(map[i*64 + j], speedcap[n]); - } - } - } - - /* assume a maximum speed for 1394b PHYs, nodemgr will correct it */ - if (local_link_speed > SELFID_SPEED_UNKNOWN) - for (i = 0; i < nodecount; i++) - if (speedcap[i] == SELFID_SPEED_UNKNOWN) - speedcap[i] = local_link_speed; -} - - -/** - * hpsb_selfid_received - hand over received selfid packet to the core - * - * For host driver module usage. Safe to use in interrupt context. - * - * The host driver should have done a successful complement check (second - * quadlet is complement of first) beforehand. - */ -void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid) -{ - if (host->in_bus_reset) { - HPSB_VERBOSE("Including SelfID 0x%x", sid); - host->topology_map[host->selfid_count++] = sid; - } else { - HPSB_NOTICE("Spurious SelfID packet (0x%08x) received from bus %d", - sid, NODEID_TO_BUS(host->node_id)); - } -} - -/** - * hpsb_selfid_complete - notify completion of SelfID stage to the core - * - * For host driver module usage. Safe to use in interrupt context, although - * quite complex; so you may want to run it in the bottom rather than top half. - * - * Notify completion of SelfID stage to the core and report new physical ID - * and whether host is root now. - */ -void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot) -{ - if (!host->in_bus_reset) - HPSB_NOTICE("SelfID completion called outside of bus reset!"); - - host->node_id = LOCAL_BUS | phyid; - host->is_root = isroot; - - if (!check_selfids(host)) { - if (host->reset_retries++ < 20) { - /* selfid stage did not complete without error */ - HPSB_NOTICE("Error in SelfID stage, resetting"); - host->in_bus_reset = 0; - /* this should work from ohci1394 now... */ - hpsb_reset_bus(host, LONG_RESET); - return; - } else { - HPSB_NOTICE("Stopping out-of-control reset loop"); - HPSB_NOTICE("Warning - topology map and speed map will not be valid"); - host->reset_retries = 0; - } - } else { - host->reset_retries = 0; - build_speed_map(host, host->node_count); - } - - HPSB_VERBOSE("selfid_complete called with successful SelfID stage " - "... irm_id: 0x%X node_id: 0x%X",host->irm_id,host->node_id); - - /* irm_id is kept up to date by check_selfids() */ - if (host->irm_id == host->node_id) { - host->is_irm = 1; - } else { - host->is_busmgr = 0; - host->is_irm = 0; - } - - if (isroot) { - host->driver->devctl(host, ACT_CYCLE_MASTER, 1); - host->is_cycmst = 1; - } - atomic_inc(&host->generation); - host->in_bus_reset = 0; - highlevel_host_reset(host); -} - -static DEFINE_SPINLOCK(pending_packets_lock); - -/** - * hpsb_packet_sent - notify core of sending a packet - * - * For host driver module usage. Safe to call from within a transmit packet - * routine. - * - * Notify core of sending a packet. Ackcode is the ack code returned for async - * transmits or ACKX_SEND_ERROR if the transmission failed completely; ACKX_NONE - * for other cases (internal errors that don't justify a panic). - */ -void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, - int ackcode) -{ - unsigned long flags; - - spin_lock_irqsave(&pending_packets_lock, flags); - - packet->ack_code = ackcode; - - if (packet->no_waiter || packet->state == hpsb_complete) { - /* if packet->no_waiter, must not have a tlabel allocated */ - spin_unlock_irqrestore(&pending_packets_lock, flags); - hpsb_free_packet(packet); - return; - } - - atomic_dec(&packet->refcnt); /* drop HC's reference */ - /* here the packet must be on the host->pending_packets queue */ - - if (ackcode != ACK_PENDING || !packet->expect_response) { - packet->state = hpsb_complete; - list_del_init(&packet->queue); - spin_unlock_irqrestore(&pending_packets_lock, flags); - queue_packet_complete(packet); - return; - } - - packet->state = hpsb_pending; - packet->sendtime = jiffies; - - spin_unlock_irqrestore(&pending_packets_lock, flags); - - mod_timer(&host->timeout, jiffies + host->timeout_interval); -} - -/** - * hpsb_send_phy_config - transmit a PHY configuration packet on the bus - * @host: host that PHY config packet gets sent through - * @rootid: root whose force_root bit should get set (-1 = don't set force_root) - * @gapcnt: gap count value to set (-1 = don't set gap count) - * - * This function sends a PHY config packet on the bus through the specified - * host. - * - * Return value: 0 for success or negative error number otherwise. - */ -int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt) -{ - struct hpsb_packet *packet; - quadlet_t d = 0; - int retval = 0; - - if (rootid >= ALL_NODES || rootid < -1 || gapcnt > 0x3f || gapcnt < -1 || - (rootid == -1 && gapcnt == -1)) { - HPSB_DEBUG("Invalid Parameter: rootid = %d gapcnt = %d", - rootid, gapcnt); - return -EINVAL; - } - - if (rootid != -1) - d |= PHYPACKET_PHYCONFIG_R | rootid << PHYPACKET_PORT_SHIFT; - if (gapcnt != -1) - d |= PHYPACKET_PHYCONFIG_T | gapcnt << PHYPACKET_GAPCOUNT_SHIFT; - - packet = hpsb_make_phypacket(host, d); - if (!packet) - return -ENOMEM; - - packet->generation = get_hpsb_generation(host); - retval = hpsb_send_packet_and_wait(packet); - hpsb_free_packet(packet); - - return retval; -} - -/** - * hpsb_send_packet - transmit a packet on the bus - * @packet: packet to send - * - * The packet is sent through the host specified in the packet->host field. - * Before sending, the packet's transmit speed is automatically determined - * using the local speed map when it is an async, non-broadcast packet. - * - * Possibilities for failure are that host is either not initialized, in bus - * reset, the packet's generation number doesn't match the current generation - * number or the host reports a transmit error. - * - * Return value: 0 on success, negative errno on failure. - */ -int hpsb_send_packet(struct hpsb_packet *packet) -{ - struct hpsb_host *host = packet->host; - - if (host->is_shutdown) - return -EINVAL; - if (host->in_bus_reset || - (packet->generation != get_hpsb_generation(host))) - return -EAGAIN; - - packet->state = hpsb_queued; - - /* This just seems silly to me */ - WARN_ON(packet->no_waiter && packet->expect_response); - - if (!packet->no_waiter || packet->expect_response) { - unsigned long flags; - - atomic_inc(&packet->refcnt); - /* Set the initial "sendtime" to 10 seconds from now, to - prevent premature expiry. If a packet takes more than - 10 seconds to hit the wire, we have bigger problems :) */ - packet->sendtime = jiffies + 10 * HZ; - spin_lock_irqsave(&pending_packets_lock, flags); - list_add_tail(&packet->queue, &host->pending_packets); - spin_unlock_irqrestore(&pending_packets_lock, flags); - } - - if (packet->node_id == host->node_id) { - /* it is a local request, so handle it locally */ - - quadlet_t *data; - size_t size = packet->data_size + packet->header_size; - - data = kmalloc(size, GFP_ATOMIC); - if (!data) { - HPSB_ERR("unable to allocate memory for concatenating header and data"); - return -ENOMEM; - } - - memcpy(data, packet->header, packet->header_size); - - if (packet->data_size) - memcpy(((u8*)data) + packet->header_size, packet->data, packet->data_size); - - dump_packet("send packet local", packet->header, packet->header_size, -1); - - hpsb_packet_sent(host, packet, packet->expect_response ? ACK_PENDING : ACK_COMPLETE); - hpsb_packet_received(host, data, size, 0); - - kfree(data); - - return 0; - } - - if (packet->type == hpsb_async && - NODEID_TO_NODE(packet->node_id) != ALL_NODES) - packet->speed_code = - host->speed[NODEID_TO_NODE(packet->node_id)]; - - dump_packet("send packet", packet->header, packet->header_size, packet->speed_code); - - return host->driver->transmit_packet(host, packet); -} - -/* We could just use complete() directly as the packet complete - * callback, but this is more typesafe, in the sense that we get a - * compiler error if the prototype for complete() changes. */ - -static void complete_packet(void *data) -{ - complete((struct completion *) data); -} - -/** - * hpsb_send_packet_and_wait - enqueue packet, block until transaction completes - * @packet: packet to send - * - * Return value: 0 on success, negative errno on failure. - */ -int hpsb_send_packet_and_wait(struct hpsb_packet *packet) -{ - struct completion done; - int retval; - - init_completion(&done); - hpsb_set_packet_complete_task(packet, complete_packet, &done); - retval = hpsb_send_packet(packet); - if (retval == 0) - wait_for_completion(&done); - - return retval; -} - -static void send_packet_nocare(struct hpsb_packet *packet) -{ - if (hpsb_send_packet(packet) < 0) { - hpsb_free_packet(packet); - } -} - -static size_t packet_size_to_data_size(size_t packet_size, size_t header_size, - size_t buffer_size, int tcode) -{ - size_t ret = packet_size <= header_size ? 0 : packet_size - header_size; - - if (unlikely(ret > buffer_size)) - ret = buffer_size; - - if (unlikely(ret + header_size != packet_size)) - HPSB_ERR("unexpected packet size %zd (tcode %d), bug?", - packet_size, tcode); - return ret; -} - -static void handle_packet_response(struct hpsb_host *host, int tcode, - quadlet_t *data, size_t size) -{ - struct hpsb_packet *packet; - int tlabel = (data[0] >> 10) & 0x3f; - size_t header_size; - unsigned long flags; - - spin_lock_irqsave(&pending_packets_lock, flags); - - list_for_each_entry(packet, &host->pending_packets, queue) - if (packet->tlabel == tlabel && - packet->node_id == (data[1] >> 16)) - goto found; - - spin_unlock_irqrestore(&pending_packets_lock, flags); - HPSB_DEBUG("unsolicited response packet received - %s", - "no tlabel match"); - dump_packet("contents", data, 16, -1); - return; - -found: - switch (packet->tcode) { - case TCODE_WRITEQ: - case TCODE_WRITEB: - if (unlikely(tcode != TCODE_WRITE_RESPONSE)) - break; - header_size = 12; - size = 0; - goto dequeue; - - case TCODE_READQ: - if (unlikely(tcode != TCODE_READQ_RESPONSE)) - break; - header_size = 16; - size = 0; - goto dequeue; - - case TCODE_READB: - if (unlikely(tcode != TCODE_READB_RESPONSE)) - break; - header_size = 16; - size = packet_size_to_data_size(size, header_size, - packet->allocated_data_size, - tcode); - goto dequeue; - - case TCODE_LOCK_REQUEST: - if (unlikely(tcode != TCODE_LOCK_RESPONSE)) - break; - header_size = 16; - size = packet_size_to_data_size(min(size, (size_t)(16 + 8)), - header_size, - packet->allocated_data_size, - tcode); - goto dequeue; - } - - spin_unlock_irqrestore(&pending_packets_lock, flags); - HPSB_DEBUG("unsolicited response packet received - %s", - "tcode mismatch"); - dump_packet("contents", data, 16, -1); - return; - -dequeue: - list_del_init(&packet->queue); - spin_unlock_irqrestore(&pending_packets_lock, flags); - - if (packet->state == hpsb_queued) { - packet->sendtime = jiffies; - packet->ack_code = ACK_PENDING; - } - packet->state = hpsb_complete; - - memcpy(packet->header, data, header_size); - if (size) - memcpy(packet->data, data + 4, size); - - queue_packet_complete(packet); -} - - -static struct hpsb_packet *create_reply_packet(struct hpsb_host *host, - quadlet_t *data, size_t dsize) -{ - struct hpsb_packet *p; - - p = hpsb_alloc_packet(dsize); - if (unlikely(p == NULL)) { - /* FIXME - send data_error response */ - HPSB_ERR("out of memory, cannot send response packet"); - return NULL; - } - - p->type = hpsb_async; - p->state = hpsb_unused; - p->host = host; - p->node_id = data[1] >> 16; - p->tlabel = (data[0] >> 10) & 0x3f; - p->no_waiter = 1; - - p->generation = get_hpsb_generation(host); - - if (dsize % 4) - p->data[dsize / 4] = 0; - - return p; -} - -#define PREP_ASYNC_HEAD_RCODE(tc) \ - packet->tcode = tc; \ - packet->header[0] = (packet->node_id << 16) | (packet->tlabel << 10) \ - | (1 << 8) | (tc << 4); \ - packet->header[1] = (packet->host->node_id << 16) | (rcode << 12); \ - packet->header[2] = 0 - -static void fill_async_readquad_resp(struct hpsb_packet *packet, int rcode, - quadlet_t data) -{ - PREP_ASYNC_HEAD_RCODE(TCODE_READQ_RESPONSE); - packet->header[3] = data; - packet->header_size = 16; - packet->data_size = 0; -} - -static void fill_async_readblock_resp(struct hpsb_packet *packet, int rcode, - int length) -{ - if (rcode != RCODE_COMPLETE) - length = 0; - - PREP_ASYNC_HEAD_RCODE(TCODE_READB_RESPONSE); - packet->header[3] = length << 16; - packet->header_size = 16; - packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0); -} - -static void fill_async_write_resp(struct hpsb_packet *packet, int rcode) -{ - PREP_ASYNC_HEAD_RCODE(TCODE_WRITE_RESPONSE); - packet->header_size = 12; - packet->data_size = 0; -} - -static void fill_async_lock_resp(struct hpsb_packet *packet, int rcode, int extcode, - int length) -{ - if (rcode != RCODE_COMPLETE) - length = 0; - - PREP_ASYNC_HEAD_RCODE(TCODE_LOCK_RESPONSE); - packet->header[3] = (length << 16) | extcode; - packet->header_size = 16; - packet->data_size = length; -} - -static void handle_incoming_packet(struct hpsb_host *host, int tcode, - quadlet_t *data, size_t size, - int write_acked) -{ - struct hpsb_packet *packet; - int length, rcode, extcode; - quadlet_t buffer; - nodeid_t source = data[1] >> 16; - nodeid_t dest = data[0] >> 16; - u16 flags = (u16) data[0]; - u64 addr; - - /* FIXME? - * Out-of-bounds lengths are left for highlevel_read|write to cap. */ - - switch (tcode) { - case TCODE_WRITEQ: - addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - rcode = highlevel_write(host, source, dest, data + 3, - addr, 4, flags); - goto handle_write_request; - - case TCODE_WRITEB: - addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - rcode = highlevel_write(host, source, dest, data + 4, - addr, data[3] >> 16, flags); -handle_write_request: - if (rcode < 0 || write_acked || - NODEID_TO_NODE(data[0] >> 16) == NODE_MASK) - return; - /* not a broadcast write, reply */ - packet = create_reply_packet(host, data, 0); - if (packet) { - fill_async_write_resp(packet, rcode); - send_packet_nocare(packet); - } - return; - - case TCODE_READQ: - addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - rcode = highlevel_read(host, source, &buffer, addr, 4, flags); - if (rcode < 0) - return; - - packet = create_reply_packet(host, data, 0); - if (packet) { - fill_async_readquad_resp(packet, rcode, buffer); - send_packet_nocare(packet); - } - return; - - case TCODE_READB: - length = data[3] >> 16; - packet = create_reply_packet(host, data, length); - if (!packet) - return; - - addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - rcode = highlevel_read(host, source, packet->data, addr, - length, flags); - if (rcode < 0) { - hpsb_free_packet(packet); - return; - } - fill_async_readblock_resp(packet, rcode, length); - send_packet_nocare(packet); - return; - - case TCODE_LOCK_REQUEST: - length = data[3] >> 16; - extcode = data[3] & 0xffff; - addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - - packet = create_reply_packet(host, data, 8); - if (!packet) - return; - - if (extcode == 0 || extcode >= 7) { - /* let switch default handle error */ - length = 0; - } - - switch (length) { - case 4: - rcode = highlevel_lock(host, source, packet->data, addr, - data[4], 0, extcode, flags); - fill_async_lock_resp(packet, rcode, extcode, 4); - break; - case 8: - if (extcode != EXTCODE_FETCH_ADD && - extcode != EXTCODE_LITTLE_ADD) { - rcode = highlevel_lock(host, source, - packet->data, addr, - data[5], data[4], - extcode, flags); - fill_async_lock_resp(packet, rcode, extcode, 4); - } else { - rcode = highlevel_lock64(host, source, - (octlet_t *)packet->data, addr, - *(octlet_t *)(data + 4), 0ULL, - extcode, flags); - fill_async_lock_resp(packet, rcode, extcode, 8); - } - break; - case 16: - rcode = highlevel_lock64(host, source, - (octlet_t *)packet->data, addr, - *(octlet_t *)(data + 6), - *(octlet_t *)(data + 4), - extcode, flags); - fill_async_lock_resp(packet, rcode, extcode, 8); - break; - default: - rcode = RCODE_TYPE_ERROR; - fill_async_lock_resp(packet, rcode, extcode, 0); - } - - if (rcode < 0) - hpsb_free_packet(packet); - else - send_packet_nocare(packet); - return; - } -} - -/** - * hpsb_packet_received - hand over received packet to the core - * - * For host driver module usage. - * - * The contents of data are expected to be the full packet but with the CRCs - * left out (data block follows header immediately), with the header (i.e. the - * first four quadlets) in machine byte order and the data block in big endian. - * *@data can be safely overwritten after this call. - * - * If the packet is a write request, @write_acked is to be set to true if it was - * ack_complete'd already, false otherwise. This argument is ignored for any - * other packet type. - */ -void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, - int write_acked) -{ - int tcode; - - if (unlikely(host->in_bus_reset)) { - HPSB_DEBUG("received packet during reset; ignoring"); - return; - } - - dump_packet("received packet", data, size, -1); - - tcode = (data[0] >> 4) & 0xf; - - switch (tcode) { - case TCODE_WRITE_RESPONSE: - case TCODE_READQ_RESPONSE: - case TCODE_READB_RESPONSE: - case TCODE_LOCK_RESPONSE: - handle_packet_response(host, tcode, data, size); - break; - - case TCODE_WRITEQ: - case TCODE_WRITEB: - case TCODE_READQ: - case TCODE_READB: - case TCODE_LOCK_REQUEST: - handle_incoming_packet(host, tcode, data, size, write_acked); - break; - - case TCODE_CYCLE_START: - /* simply ignore this packet if it is passed on */ - break; - - default: - HPSB_DEBUG("received packet with bogus transaction code %d", - tcode); - break; - } -} - -static void abort_requests(struct hpsb_host *host) -{ - struct hpsb_packet *packet, *p; - struct list_head tmp; - unsigned long flags; - - host->driver->devctl(host, CANCEL_REQUESTS, 0); - - INIT_LIST_HEAD(&tmp); - spin_lock_irqsave(&pending_packets_lock, flags); - list_splice_init(&host->pending_packets, &tmp); - spin_unlock_irqrestore(&pending_packets_lock, flags); - - list_for_each_entry_safe(packet, p, &tmp, queue) { - list_del_init(&packet->queue); - packet->state = hpsb_complete; - packet->ack_code = ACKX_ABORTED; - queue_packet_complete(packet); - } -} - -void abort_timedouts(unsigned long __opaque) -{ - struct hpsb_host *host = (struct hpsb_host *)__opaque; - struct hpsb_packet *packet, *p; - struct list_head tmp; - unsigned long flags, expire, j; - - spin_lock_irqsave(&host->csr.lock, flags); - expire = host->csr.expire; - spin_unlock_irqrestore(&host->csr.lock, flags); - - j = jiffies; - INIT_LIST_HEAD(&tmp); - spin_lock_irqsave(&pending_packets_lock, flags); - - list_for_each_entry_safe(packet, p, &host->pending_packets, queue) { - if (time_before(packet->sendtime + expire, j)) - list_move_tail(&packet->queue, &tmp); - else - /* Since packets are added to the tail, the oldest - * ones are first, always. When we get to one that - * isn't timed out, the rest aren't either. */ - break; - } - if (!list_empty(&host->pending_packets)) - mod_timer(&host->timeout, j + host->timeout_interval); - - spin_unlock_irqrestore(&pending_packets_lock, flags); - - list_for_each_entry_safe(packet, p, &tmp, queue) { - list_del_init(&packet->queue); - packet->state = hpsb_complete; - packet->ack_code = ACKX_TIMEOUT; - queue_packet_complete(packet); - } -} - -static struct task_struct *khpsbpkt_thread; -static LIST_HEAD(hpsbpkt_queue); - -static void queue_packet_complete(struct hpsb_packet *packet) -{ - unsigned long flags; - - if (packet->no_waiter) { - hpsb_free_packet(packet); - return; - } - if (packet->complete_routine != NULL) { - spin_lock_irqsave(&pending_packets_lock, flags); - list_add_tail(&packet->queue, &hpsbpkt_queue); - spin_unlock_irqrestore(&pending_packets_lock, flags); - wake_up_process(khpsbpkt_thread); - } - return; -} - -/* - * Kernel thread which handles packets that are completed. This way the - * packet's "complete" function is asynchronously run in process context. - * Only packets which have a "complete" function may be sent here. - */ -static int hpsbpkt_thread(void *__hi) -{ - struct hpsb_packet *packet, *p; - struct list_head tmp; - int may_schedule; - - while (!kthread_should_stop()) { - - INIT_LIST_HEAD(&tmp); - spin_lock_irq(&pending_packets_lock); - list_splice_init(&hpsbpkt_queue, &tmp); - spin_unlock_irq(&pending_packets_lock); - - list_for_each_entry_safe(packet, p, &tmp, queue) { - list_del_init(&packet->queue); - packet->complete_routine(packet->complete_data); - } - - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irq(&pending_packets_lock); - may_schedule = list_empty(&hpsbpkt_queue); - spin_unlock_irq(&pending_packets_lock); - if (may_schedule) - schedule(); - __set_current_state(TASK_RUNNING); - } - return 0; -} - -static int __init ieee1394_init(void) -{ - int i, ret; - - /* non-fatal error */ - if (hpsb_init_config_roms()) { - HPSB_ERR("Failed to initialize some config rom entries.\n"); - HPSB_ERR("Some features may not be available\n"); - } - - khpsbpkt_thread = kthread_run(hpsbpkt_thread, NULL, "khpsbpkt"); - if (IS_ERR(khpsbpkt_thread)) { - HPSB_ERR("Failed to start hpsbpkt thread!\n"); - ret = PTR_ERR(khpsbpkt_thread); - goto exit_cleanup_config_roms; - } - - if (register_chrdev_region(IEEE1394_CORE_DEV, 256, "ieee1394")) { - HPSB_ERR("unable to register character device major %d!\n", IEEE1394_MAJOR); - ret = -ENODEV; - goto exit_release_kernel_thread; - } - - ret = bus_register(&ieee1394_bus_type); - if (ret < 0) { - HPSB_INFO("bus register failed"); - goto release_chrdev; - } - - for (i = 0; fw_bus_attrs[i]; i++) { - ret = bus_create_file(&ieee1394_bus_type, fw_bus_attrs[i]); - if (ret < 0) { - while (i >= 0) { - bus_remove_file(&ieee1394_bus_type, - fw_bus_attrs[i--]); - } - bus_unregister(&ieee1394_bus_type); - goto release_chrdev; - } - } - - ret = class_register(&hpsb_host_class); - if (ret < 0) - goto release_all_bus; - - hpsb_protocol_class = class_create(THIS_MODULE, "ieee1394_protocol"); - if (IS_ERR(hpsb_protocol_class)) { - ret = PTR_ERR(hpsb_protocol_class); - goto release_class_host; - } - - ret = init_csr(); - if (ret) { - HPSB_INFO("init csr failed"); - ret = -ENOMEM; - goto release_class_protocol; - } - - if (disable_nodemgr) { - HPSB_INFO("nodemgr and IRM functionality disabled"); - /* We shouldn't contend for IRM with nodemgr disabled, since - nodemgr implements functionality required of ieee1394a-2000 - IRMs */ - hpsb_disable_irm = 1; - - return 0; - } - - if (hpsb_disable_irm) { - HPSB_INFO("IRM functionality disabled"); - } - - ret = init_ieee1394_nodemgr(); - if (ret < 0) { - HPSB_INFO("init nodemgr failed"); - goto cleanup_csr; - } - - return 0; - -cleanup_csr: - cleanup_csr(); -release_class_protocol: - class_destroy(hpsb_protocol_class); -release_class_host: - class_unregister(&hpsb_host_class); -release_all_bus: - for (i = 0; fw_bus_attrs[i]; i++) - bus_remove_file(&ieee1394_bus_type, fw_bus_attrs[i]); - bus_unregister(&ieee1394_bus_type); -release_chrdev: - unregister_chrdev_region(IEEE1394_CORE_DEV, 256); -exit_release_kernel_thread: - kthread_stop(khpsbpkt_thread); -exit_cleanup_config_roms: - hpsb_cleanup_config_roms(); - return ret; -} - -static void __exit ieee1394_cleanup(void) -{ - int i; - - if (!disable_nodemgr) - cleanup_ieee1394_nodemgr(); - - cleanup_csr(); - - class_destroy(hpsb_protocol_class); - class_unregister(&hpsb_host_class); - for (i = 0; fw_bus_attrs[i]; i++) - bus_remove_file(&ieee1394_bus_type, fw_bus_attrs[i]); - bus_unregister(&ieee1394_bus_type); - - kthread_stop(khpsbpkt_thread); - - hpsb_cleanup_config_roms(); - - unregister_chrdev_region(IEEE1394_CORE_DEV, 256); -} - -fs_initcall(ieee1394_init); -module_exit(ieee1394_cleanup); - -/* Exported symbols */ - -/** hosts.c **/ -EXPORT_SYMBOL(hpsb_alloc_host); -EXPORT_SYMBOL(hpsb_add_host); -EXPORT_SYMBOL(hpsb_resume_host); -EXPORT_SYMBOL(hpsb_remove_host); -EXPORT_SYMBOL(hpsb_update_config_rom_image); - -/** ieee1394_core.c **/ -EXPORT_SYMBOL(hpsb_speedto_str); -EXPORT_SYMBOL(hpsb_protocol_class); -EXPORT_SYMBOL(hpsb_set_packet_complete_task); -EXPORT_SYMBOL(hpsb_alloc_packet); -EXPORT_SYMBOL(hpsb_free_packet); -EXPORT_SYMBOL(hpsb_send_packet); -EXPORT_SYMBOL(hpsb_reset_bus); -EXPORT_SYMBOL(hpsb_read_cycle_timer); -EXPORT_SYMBOL(hpsb_bus_reset); -EXPORT_SYMBOL(hpsb_selfid_received); -EXPORT_SYMBOL(hpsb_selfid_complete); -EXPORT_SYMBOL(hpsb_packet_sent); -EXPORT_SYMBOL(hpsb_packet_received); -EXPORT_SYMBOL_GPL(hpsb_disable_irm); - -/** ieee1394_transactions.c **/ -EXPORT_SYMBOL(hpsb_get_tlabel); -EXPORT_SYMBOL(hpsb_free_tlabel); -EXPORT_SYMBOL(hpsb_make_readpacket); -EXPORT_SYMBOL(hpsb_make_writepacket); -EXPORT_SYMBOL(hpsb_make_streampacket); -EXPORT_SYMBOL(hpsb_make_lockpacket); -EXPORT_SYMBOL(hpsb_make_lock64packet); -EXPORT_SYMBOL(hpsb_make_phypacket); -EXPORT_SYMBOL(hpsb_read); -EXPORT_SYMBOL(hpsb_write); -EXPORT_SYMBOL(hpsb_lock); -EXPORT_SYMBOL(hpsb_packet_success); - -/** highlevel.c **/ -EXPORT_SYMBOL(hpsb_register_highlevel); -EXPORT_SYMBOL(hpsb_unregister_highlevel); -EXPORT_SYMBOL(hpsb_register_addrspace); -EXPORT_SYMBOL(hpsb_unregister_addrspace); -EXPORT_SYMBOL(hpsb_allocate_and_register_addrspace); -EXPORT_SYMBOL(hpsb_get_hostinfo); -EXPORT_SYMBOL(hpsb_create_hostinfo); -EXPORT_SYMBOL(hpsb_destroy_hostinfo); -EXPORT_SYMBOL(hpsb_set_hostinfo_key); -EXPORT_SYMBOL(hpsb_get_hostinfo_bykey); -EXPORT_SYMBOL(hpsb_set_hostinfo); - -/** nodemgr.c **/ -EXPORT_SYMBOL(hpsb_node_fill_packet); -EXPORT_SYMBOL(hpsb_node_write); -EXPORT_SYMBOL(__hpsb_register_protocol); -EXPORT_SYMBOL(hpsb_unregister_protocol); - -/** csr.c **/ -EXPORT_SYMBOL(hpsb_update_config_rom); - -/** dma.c **/ -EXPORT_SYMBOL(dma_prog_region_init); -EXPORT_SYMBOL(dma_prog_region_alloc); -EXPORT_SYMBOL(dma_prog_region_free); -EXPORT_SYMBOL(dma_region_init); -EXPORT_SYMBOL(dma_region_alloc); -EXPORT_SYMBOL(dma_region_free); -EXPORT_SYMBOL(dma_region_sync_for_cpu); -EXPORT_SYMBOL(dma_region_sync_for_device); -EXPORT_SYMBOL(dma_region_mmap); -EXPORT_SYMBOL(dma_region_offset_to_bus); - -/** iso.c **/ -EXPORT_SYMBOL(hpsb_iso_xmit_init); -EXPORT_SYMBOL(hpsb_iso_recv_init); -EXPORT_SYMBOL(hpsb_iso_xmit_start); -EXPORT_SYMBOL(hpsb_iso_recv_start); -EXPORT_SYMBOL(hpsb_iso_recv_listen_channel); -EXPORT_SYMBOL(hpsb_iso_recv_unlisten_channel); -EXPORT_SYMBOL(hpsb_iso_recv_set_channel_mask); -EXPORT_SYMBOL(hpsb_iso_stop); -EXPORT_SYMBOL(hpsb_iso_shutdown); -EXPORT_SYMBOL(hpsb_iso_xmit_queue_packet); -EXPORT_SYMBOL(hpsb_iso_xmit_sync); -EXPORT_SYMBOL(hpsb_iso_recv_release_packets); -EXPORT_SYMBOL(hpsb_iso_n_ready); -EXPORT_SYMBOL(hpsb_iso_packet_sent); -EXPORT_SYMBOL(hpsb_iso_packet_received); -EXPORT_SYMBOL(hpsb_iso_wake); -EXPORT_SYMBOL(hpsb_iso_recv_flush); - -/** csr1212.c **/ -EXPORT_SYMBOL(csr1212_attach_keyval_to_directory); -EXPORT_SYMBOL(csr1212_detach_keyval_from_directory); -EXPORT_SYMBOL(csr1212_get_keyval); -EXPORT_SYMBOL(csr1212_new_directory); -EXPORT_SYMBOL(csr1212_parse_keyval); -EXPORT_SYMBOL(csr1212_read); -EXPORT_SYMBOL(csr1212_release_keyval); diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h deleted file mode 100644 index 28b9f58bafd2..000000000000 --- a/drivers/ieee1394/ieee1394_core.h +++ /dev/null @@ -1,172 +0,0 @@ -#ifndef _IEEE1394_CORE_H -#define _IEEE1394_CORE_H - -#include -#include -#include -#include -#include -#include - -#include "hosts.h" -#include "ieee1394_types.h" - -struct hpsb_packet { - /* This struct is basically read-only for hosts with the exception of - * the data buffer contents and driver_list. */ - - /* This can be used for host driver internal linking. - * - * NOTE: This must be left in init state when the driver is done - * with it (e.g. by using list_del_init()), since the core does - * some sanity checks to make sure the packet is not on a - * driver_list when free'ing it. */ - struct list_head driver_list; - - nodeid_t node_id; - - /* hpsb_raw = send as-is, do not CRC (but still byte-swap it) */ - enum { hpsb_async, hpsb_raw } __attribute__((packed)) type; - - /* Okay, this is core internal and a no care for hosts. - * queued = queued for sending - * pending = sent, waiting for response - * complete = processing completed, successful or not - */ - enum { - hpsb_unused, hpsb_queued, hpsb_pending, hpsb_complete - } __attribute__((packed)) state; - - /* These are core-internal. */ - signed char tlabel; - signed char ack_code; - unsigned char tcode; - - unsigned expect_response:1; - unsigned no_waiter:1; - - /* Speed to transmit with: 0 = 100Mbps, 1 = 200Mbps, 2 = 400Mbps */ - unsigned speed_code:2; - - struct hpsb_host *host; - unsigned int generation; - - atomic_t refcnt; - struct list_head queue; - - /* Function (and possible data to pass to it) to call when this - * packet is completed. */ - void (*complete_routine)(void *); - void *complete_data; - - /* Store jiffies for implementing bus timeouts. */ - unsigned long sendtime; - - /* Core-internal. */ - size_t allocated_data_size; /* as allocated */ - - /* Sizes are in bytes. To be set by caller of hpsb_alloc_packet. */ - size_t data_size; /* as filled in */ - size_t header_size; /* as filled in, not counting the CRC */ - - /* Buffers */ - quadlet_t *data; /* can be DMA-mapped */ - quadlet_t header[5]; - quadlet_t embedded_data[0]; /* keep as last member */ -}; - -void hpsb_set_packet_complete_task(struct hpsb_packet *packet, - void (*routine)(void *), void *data); -static inline struct hpsb_packet *driver_packet(struct list_head *l) -{ - return list_entry(l, struct hpsb_packet, driver_list); -} -void abort_timedouts(unsigned long __opaque); -struct hpsb_packet *hpsb_alloc_packet(size_t data_size); -void hpsb_free_packet(struct hpsb_packet *packet); - -/** - * get_hpsb_generation - generation counter for the complete 1394 subsystem - * - * Generation gets incremented on every change in the subsystem (notably on bus - * resets). Use the functions, not the variable. - */ -static inline unsigned int get_hpsb_generation(struct hpsb_host *host) -{ - return atomic_read(&host->generation); -} - -int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt); -int hpsb_send_packet(struct hpsb_packet *packet); -int hpsb_send_packet_and_wait(struct hpsb_packet *packet); -int hpsb_reset_bus(struct hpsb_host *host, int type); -int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer, - u64 *local_time); - -int hpsb_bus_reset(struct hpsb_host *host); -void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid); -void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot); -void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, - int ackcode); -void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, - int write_acked); - -/* - * CHARACTER DEVICE DISPATCHING - * - * All ieee1394 character device drivers share the same major number - * (major 171). The 256 minor numbers are allocated to the various - * task-specific interfaces (raw1394, video1394, dv1394, etc) in - * blocks of 16. - * - * The core ieee1394.o module allocates the device number region - * 171:0-255, the various drivers must then cdev_add() their cdev - * objects to handle their respective sub-regions. - * - * Minor device number block allocations: - * - * Block 0 ( 0- 15) raw1394 - * Block 1 ( 16- 31) video1394 - * Block 2 ( 32- 47) dv1394 - * - * Blocks 3-14 free for future allocation - * - * Block 15 (240-255) reserved for drivers under development, etc. - */ - -#define IEEE1394_MAJOR 171 - -#define IEEE1394_MINOR_BLOCK_RAW1394 0 -#define IEEE1394_MINOR_BLOCK_VIDEO1394 1 -#define IEEE1394_MINOR_BLOCK_DV1394 2 -#define IEEE1394_MINOR_BLOCK_EXPERIMENTAL 15 - -#define IEEE1394_CORE_DEV MKDEV(IEEE1394_MAJOR, 0) -#define IEEE1394_RAW1394_DEV MKDEV(IEEE1394_MAJOR, \ - IEEE1394_MINOR_BLOCK_RAW1394 * 16) -#define IEEE1394_VIDEO1394_DEV MKDEV(IEEE1394_MAJOR, \ - IEEE1394_MINOR_BLOCK_VIDEO1394 * 16) -#define IEEE1394_DV1394_DEV MKDEV(IEEE1394_MAJOR, \ - IEEE1394_MINOR_BLOCK_DV1394 * 16) -#define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, \ - IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16) - -/** - * ieee1394_file_to_instance - get the index within a minor number block - */ -static inline unsigned char ieee1394_file_to_instance(struct file *file) -{ - int idx = cdev_index(file->f_path.dentry->d_inode); - if (idx < 0) - idx = 0; - return idx; -} - -extern int hpsb_disable_irm; - -/* Our sysfs bus entry */ -extern struct bus_type ieee1394_bus_type; -extern struct class hpsb_host_class; -extern struct class *hpsb_protocol_class; - -#endif /* _IEEE1394_CORE_H */ diff --git a/drivers/ieee1394/ieee1394_hotplug.h b/drivers/ieee1394/ieee1394_hotplug.h deleted file mode 100644 index dd5500ed8322..000000000000 --- a/drivers/ieee1394/ieee1394_hotplug.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _IEEE1394_HOTPLUG_H -#define _IEEE1394_HOTPLUG_H - -/* Unit spec id and sw version entry for some protocols */ -#define AVC_UNIT_SPEC_ID_ENTRY 0x0000A02D -#define AVC_SW_VERSION_ENTRY 0x00010001 -#define CAMERA_UNIT_SPEC_ID_ENTRY 0x0000A02D -#define CAMERA_SW_VERSION_ENTRY 0x00000100 - -/* /include/linux/mod_devicetable.h defines: - * IEEE1394_MATCH_VENDOR_ID - * IEEE1394_MATCH_MODEL_ID - * IEEE1394_MATCH_SPECIFIER_ID - * IEEE1394_MATCH_VERSION - * struct ieee1394_device_id - */ -#include - -#endif /* _IEEE1394_HOTPLUG_H */ diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c deleted file mode 100644 index 675b3135d5f1..000000000000 --- a/drivers/ieee1394/ieee1394_transactions.c +++ /dev/null @@ -1,595 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * Transaction support. - * - * Copyright (C) 1999 Andreas E. Bombe - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#include -#include -#include -#include -#include -#include /* because linux/wait.h is broken if CONFIG_SMP=n */ -#include - -#include -#include -#include - -#include "ieee1394.h" -#include "ieee1394_types.h" -#include "hosts.h" -#include "ieee1394_core.h" -#include "ieee1394_transactions.h" - -#define PREP_ASYNC_HEAD_ADDRESS(tc) \ - packet->tcode = tc; \ - packet->header[0] = (packet->node_id << 16) | (packet->tlabel << 10) \ - | (1 << 8) | (tc << 4); \ - packet->header[1] = (packet->host->node_id << 16) | (addr >> 32); \ - packet->header[2] = addr & 0xffffffff - -#ifndef HPSB_DEBUG_TLABELS -static -#endif -DEFINE_SPINLOCK(hpsb_tlabel_lock); - -static DECLARE_WAIT_QUEUE_HEAD(tlabel_wq); - -static void fill_async_readquad(struct hpsb_packet *packet, u64 addr) -{ - PREP_ASYNC_HEAD_ADDRESS(TCODE_READQ); - packet->header_size = 12; - packet->data_size = 0; - packet->expect_response = 1; -} - -static void fill_async_readblock(struct hpsb_packet *packet, u64 addr, - int length) -{ - PREP_ASYNC_HEAD_ADDRESS(TCODE_READB); - packet->header[3] = length << 16; - packet->header_size = 16; - packet->data_size = 0; - packet->expect_response = 1; -} - -static void fill_async_writequad(struct hpsb_packet *packet, u64 addr, - quadlet_t data) -{ - PREP_ASYNC_HEAD_ADDRESS(TCODE_WRITEQ); - packet->header[3] = data; - packet->header_size = 16; - packet->data_size = 0; - packet->expect_response = 1; -} - -static void fill_async_writeblock(struct hpsb_packet *packet, u64 addr, - int length) -{ - PREP_ASYNC_HEAD_ADDRESS(TCODE_WRITEB); - packet->header[3] = length << 16; - packet->header_size = 16; - packet->expect_response = 1; - packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0); -} - -static void fill_async_lock(struct hpsb_packet *packet, u64 addr, int extcode, - int length) -{ - PREP_ASYNC_HEAD_ADDRESS(TCODE_LOCK_REQUEST); - packet->header[3] = (length << 16) | extcode; - packet->header_size = 16; - packet->data_size = length; - packet->expect_response = 1; -} - -static void fill_phy_packet(struct hpsb_packet *packet, quadlet_t data) -{ - packet->header[0] = data; - packet->header[1] = ~data; - packet->header_size = 8; - packet->data_size = 0; - packet->expect_response = 0; - packet->type = hpsb_raw; /* No CRC added */ - packet->speed_code = IEEE1394_SPEED_100; /* Force speed to be 100Mbps */ -} - -static void fill_async_stream_packet(struct hpsb_packet *packet, int length, - int channel, int tag, int sync) -{ - packet->header[0] = (length << 16) | (tag << 14) | (channel << 8) - | (TCODE_STREAM_DATA << 4) | sync; - - packet->header_size = 4; - packet->data_size = length; - packet->type = hpsb_async; - packet->tcode = TCODE_ISO_DATA; -} - -/* same as hpsb_get_tlabel, except that it returns immediately */ -static int hpsb_get_tlabel_atomic(struct hpsb_packet *packet) -{ - unsigned long flags, *tp; - u8 *next; - int tlabel, n = NODEID_TO_NODE(packet->node_id); - - /* Broadcast transactions are complete once the request has been sent. - * Use the same transaction label for all broadcast transactions. */ - if (unlikely(n == ALL_NODES)) { - packet->tlabel = 0; - return 0; - } - tp = packet->host->tl_pool[n].map; - next = &packet->host->next_tl[n]; - - spin_lock_irqsave(&hpsb_tlabel_lock, flags); - tlabel = find_next_zero_bit(tp, 64, *next); - if (tlabel > 63) - tlabel = find_first_zero_bit(tp, 64); - if (tlabel > 63) { - spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); - return -EAGAIN; - } - __set_bit(tlabel, tp); - *next = (tlabel + 1) & 63; - spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); - - packet->tlabel = tlabel; - return 0; -} - -/** - * hpsb_get_tlabel - allocate a transaction label - * @packet: the packet whose tlabel and tl_pool we set - * - * Every asynchronous transaction on the 1394 bus needs a transaction - * label to match the response to the request. This label has to be - * different from any other transaction label in an outstanding request to - * the same node to make matching possible without ambiguity. - * - * There are 64 different tlabels, so an allocated tlabel has to be freed - * with hpsb_free_tlabel() after the transaction is complete (unless it's - * reused again for the same target node). - * - * Return value: Zero on success, otherwise non-zero. A non-zero return - * generally means there are no available tlabels. If this is called out - * of interrupt or atomic context, then it will sleep until can return a - * tlabel or a signal is received. - */ -int hpsb_get_tlabel(struct hpsb_packet *packet) -{ - if (irqs_disabled() || in_atomic()) - return hpsb_get_tlabel_atomic(packet); - - /* NB: The macro wait_event_interruptible() is called with a condition - * argument with side effect. This is only possible because the side - * effect does not occur until the condition became true, and - * wait_event_interruptible() won't evaluate the condition again after - * that. */ - return wait_event_interruptible(tlabel_wq, - !hpsb_get_tlabel_atomic(packet)); -} - -/** - * hpsb_free_tlabel - free an allocated transaction label - * @packet: packet whose tlabel and tl_pool needs to be cleared - * - * Frees the transaction label allocated with hpsb_get_tlabel(). The - * tlabel has to be freed after the transaction is complete (i.e. response - * was received for a split transaction or packet was sent for a unified - * transaction). - * - * A tlabel must not be freed twice. - */ -void hpsb_free_tlabel(struct hpsb_packet *packet) -{ - unsigned long flags, *tp; - int tlabel, n = NODEID_TO_NODE(packet->node_id); - - if (unlikely(n == ALL_NODES)) - return; - tp = packet->host->tl_pool[n].map; - tlabel = packet->tlabel; - BUG_ON(tlabel > 63 || tlabel < 0); - - spin_lock_irqsave(&hpsb_tlabel_lock, flags); - BUG_ON(!__test_and_clear_bit(tlabel, tp)); - spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); - - wake_up_interruptible(&tlabel_wq); -} - -/** - * hpsb_packet_success - Make sense of the ack and reply codes - * - * Make sense of the ack and reply codes and return more convenient error codes: - * 0 = success. -%EBUSY = node is busy, try again. -%EAGAIN = error which can - * probably resolved by retry. -%EREMOTEIO = node suffers from an internal - * error. -%EACCES = this transaction is not allowed on requested address. - * -%EINVAL = invalid address at node. - */ -int hpsb_packet_success(struct hpsb_packet *packet) -{ - switch (packet->ack_code) { - case ACK_PENDING: - switch ((packet->header[1] >> 12) & 0xf) { - case RCODE_COMPLETE: - return 0; - case RCODE_CONFLICT_ERROR: - return -EAGAIN; - case RCODE_DATA_ERROR: - return -EREMOTEIO; - case RCODE_TYPE_ERROR: - return -EACCES; - case RCODE_ADDRESS_ERROR: - return -EINVAL; - default: - HPSB_ERR("received reserved rcode %d from node %d", - (packet->header[1] >> 12) & 0xf, - packet->node_id); - return -EAGAIN; - } - - case ACK_BUSY_X: - case ACK_BUSY_A: - case ACK_BUSY_B: - return -EBUSY; - - case ACK_TYPE_ERROR: - return -EACCES; - - case ACK_COMPLETE: - if (packet->tcode == TCODE_WRITEQ - || packet->tcode == TCODE_WRITEB) { - return 0; - } else { - HPSB_ERR("impossible ack_complete from node %d " - "(tcode %d)", packet->node_id, packet->tcode); - return -EAGAIN; - } - - case ACK_DATA_ERROR: - if (packet->tcode == TCODE_WRITEB - || packet->tcode == TCODE_LOCK_REQUEST) { - return -EAGAIN; - } else { - HPSB_ERR("impossible ack_data_error from node %d " - "(tcode %d)", packet->node_id, packet->tcode); - return -EAGAIN; - } - - case ACK_ADDRESS_ERROR: - return -EINVAL; - - case ACK_TARDY: - case ACK_CONFLICT_ERROR: - case ACKX_NONE: - case ACKX_SEND_ERROR: - case ACKX_ABORTED: - case ACKX_TIMEOUT: - /* error while sending */ - return -EAGAIN; - - default: - HPSB_ERR("got invalid ack %d from node %d (tcode %d)", - packet->ack_code, packet->node_id, packet->tcode); - return -EAGAIN; - } -} - -struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node, - u64 addr, size_t length) -{ - struct hpsb_packet *packet; - - if (length == 0) - return NULL; - - packet = hpsb_alloc_packet(length); - if (!packet) - return NULL; - - packet->host = host; - packet->node_id = node; - - if (hpsb_get_tlabel(packet)) { - hpsb_free_packet(packet); - return NULL; - } - - if (length == 4) - fill_async_readquad(packet, addr); - else - fill_async_readblock(packet, addr, length); - - return packet; -} - -struct hpsb_packet *hpsb_make_writepacket(struct hpsb_host *host, nodeid_t node, - u64 addr, quadlet_t * buffer, - size_t length) -{ - struct hpsb_packet *packet; - - if (length == 0) - return NULL; - - packet = hpsb_alloc_packet(length); - if (!packet) - return NULL; - - if (length % 4) { /* zero padding bytes */ - packet->data[length >> 2] = 0; - } - packet->host = host; - packet->node_id = node; - - if (hpsb_get_tlabel(packet)) { - hpsb_free_packet(packet); - return NULL; - } - - if (length == 4) { - fill_async_writequad(packet, addr, buffer ? *buffer : 0); - } else { - fill_async_writeblock(packet, addr, length); - if (buffer) - memcpy(packet->data, buffer, length); - } - - return packet; -} - -struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 * buffer, - int length, int channel, int tag, - int sync) -{ - struct hpsb_packet *packet; - - if (length == 0) - return NULL; - - packet = hpsb_alloc_packet(length); - if (!packet) - return NULL; - - if (length % 4) { /* zero padding bytes */ - packet->data[length >> 2] = 0; - } - packet->host = host; - - /* Because it is too difficult to determine all PHY speeds and link - * speeds here, we use S100... */ - packet->speed_code = IEEE1394_SPEED_100; - - /* ...and prevent hpsb_send_packet() from overriding it. */ - packet->node_id = LOCAL_BUS | ALL_NODES; - - if (hpsb_get_tlabel(packet)) { - hpsb_free_packet(packet); - return NULL; - } - - fill_async_stream_packet(packet, length, channel, tag, sync); - if (buffer) - memcpy(packet->data, buffer, length); - - return packet; -} - -struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node, - u64 addr, int extcode, - quadlet_t * data, quadlet_t arg) -{ - struct hpsb_packet *p; - u32 length; - - p = hpsb_alloc_packet(8); - if (!p) - return NULL; - - p->host = host; - p->node_id = node; - if (hpsb_get_tlabel(p)) { - hpsb_free_packet(p); - return NULL; - } - - switch (extcode) { - case EXTCODE_FETCH_ADD: - case EXTCODE_LITTLE_ADD: - length = 4; - if (data) - p->data[0] = *data; - break; - default: - length = 8; - if (data) { - p->data[0] = arg; - p->data[1] = *data; - } - break; - } - fill_async_lock(p, addr, extcode, length); - - return p; -} - -struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host, - nodeid_t node, u64 addr, int extcode, - octlet_t * data, octlet_t arg) -{ - struct hpsb_packet *p; - u32 length; - - p = hpsb_alloc_packet(16); - if (!p) - return NULL; - - p->host = host; - p->node_id = node; - if (hpsb_get_tlabel(p)) { - hpsb_free_packet(p); - return NULL; - } - - switch (extcode) { - case EXTCODE_FETCH_ADD: - case EXTCODE_LITTLE_ADD: - length = 8; - if (data) { - p->data[0] = *data >> 32; - p->data[1] = *data & 0xffffffff; - } - break; - default: - length = 16; - if (data) { - p->data[0] = arg >> 32; - p->data[1] = arg & 0xffffffff; - p->data[2] = *data >> 32; - p->data[3] = *data & 0xffffffff; - } - break; - } - fill_async_lock(p, addr, extcode, length); - - return p; -} - -struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, quadlet_t data) -{ - struct hpsb_packet *p; - - p = hpsb_alloc_packet(0); - if (!p) - return NULL; - - p->host = host; - fill_phy_packet(p, data); - - return p; -} - -/* - * FIXME - these functions should probably read from / write to user space to - * avoid in kernel buffers for user space callers - */ - -/** - * hpsb_read - generic read function - * - * Recognizes the local node ID and act accordingly. Automatically uses a - * quadlet read request if @length == 4 and and a block read request otherwise. - * It does not yet support lengths that are not a multiple of 4. - * - * You must explicitly specifiy the @generation for which the node ID is valid, - * to avoid sending packets to the wrong nodes when we race with a bus reset. - */ -int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, - u64 addr, quadlet_t * buffer, size_t length) -{ - struct hpsb_packet *packet; - int retval = 0; - - if (length == 0) - return -EINVAL; - - packet = hpsb_make_readpacket(host, node, addr, length); - - if (!packet) { - return -ENOMEM; - } - - packet->generation = generation; - retval = hpsb_send_packet_and_wait(packet); - if (retval < 0) - goto hpsb_read_fail; - - retval = hpsb_packet_success(packet); - - if (retval == 0) { - if (length == 4) { - *buffer = packet->header[3]; - } else { - memcpy(buffer, packet->data, length); - } - } - - hpsb_read_fail: - hpsb_free_tlabel(packet); - hpsb_free_packet(packet); - - return retval; -} - -/** - * hpsb_write - generic write function - * - * Recognizes the local node ID and act accordingly. Automatically uses a - * quadlet write request if @length == 4 and and a block write request - * otherwise. It does not yet support lengths that are not a multiple of 4. - * - * You must explicitly specifiy the @generation for which the node ID is valid, - * to avoid sending packets to the wrong nodes when we race with a bus reset. - */ -int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, - u64 addr, quadlet_t * buffer, size_t length) -{ - struct hpsb_packet *packet; - int retval; - - if (length == 0) - return -EINVAL; - - packet = hpsb_make_writepacket(host, node, addr, buffer, length); - - if (!packet) - return -ENOMEM; - - packet->generation = generation; - retval = hpsb_send_packet_and_wait(packet); - if (retval < 0) - goto hpsb_write_fail; - - retval = hpsb_packet_success(packet); - - hpsb_write_fail: - hpsb_free_tlabel(packet); - hpsb_free_packet(packet); - - return retval; -} - -int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, - u64 addr, int extcode, quadlet_t *data, quadlet_t arg) -{ - struct hpsb_packet *packet; - int retval = 0; - - packet = hpsb_make_lockpacket(host, node, addr, extcode, data, arg); - if (!packet) - return -ENOMEM; - - packet->generation = generation; - retval = hpsb_send_packet_and_wait(packet); - if (retval < 0) - goto hpsb_lock_fail; - - retval = hpsb_packet_success(packet); - - if (retval == 0) - *data = packet->data[0]; - -hpsb_lock_fail: - hpsb_free_tlabel(packet); - hpsb_free_packet(packet); - - return retval; -} diff --git a/drivers/ieee1394/ieee1394_transactions.h b/drivers/ieee1394/ieee1394_transactions.h deleted file mode 100644 index 20b693be14b2..000000000000 --- a/drivers/ieee1394/ieee1394_transactions.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef _IEEE1394_TRANSACTIONS_H -#define _IEEE1394_TRANSACTIONS_H - -#include - -#include "ieee1394_types.h" - -struct hpsb_packet; -struct hpsb_host; - -int hpsb_get_tlabel(struct hpsb_packet *packet); -void hpsb_free_tlabel(struct hpsb_packet *packet); -struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node, - u64 addr, size_t length); -struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node, - u64 addr, int extcode, quadlet_t *data, - quadlet_t arg); -struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host, - nodeid_t node, u64 addr, int extcode, - octlet_t *data, octlet_t arg); -struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, quadlet_t data); -struct hpsb_packet *hpsb_make_writepacket(struct hpsb_host *host, - nodeid_t node, u64 addr, - quadlet_t *buffer, size_t length); -struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 *buffer, - int length, int channel, int tag, - int sync); -int hpsb_packet_success(struct hpsb_packet *packet); -int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, - u64 addr, quadlet_t *buffer, size_t length); -int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, - u64 addr, quadlet_t *buffer, size_t length); -int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, - u64 addr, int extcode, quadlet_t *data, quadlet_t arg); - -#ifdef HPSB_DEBUG_TLABELS -extern spinlock_t hpsb_tlabel_lock; -#endif - -#endif /* _IEEE1394_TRANSACTIONS_H */ diff --git a/drivers/ieee1394/ieee1394_types.h b/drivers/ieee1394/ieee1394_types.h deleted file mode 100644 index 9803aaa15be0..000000000000 --- a/drivers/ieee1394/ieee1394_types.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef _IEEE1394_TYPES_H -#define _IEEE1394_TYPES_H - -#include -#include -#include -#include - -typedef u32 quadlet_t; -typedef u64 octlet_t; -typedef u16 nodeid_t; - -typedef u8 byte_t; -typedef u64 nodeaddr_t; -typedef u16 arm_length_t; - -#define BUS_MASK 0xffc0 -#define BUS_SHIFT 6 -#define NODE_MASK 0x003f -#define LOCAL_BUS 0xffc0 -#define ALL_NODES 0x003f - -#define NODEID_TO_BUS(nodeid) ((nodeid & BUS_MASK) >> BUS_SHIFT) -#define NODEID_TO_NODE(nodeid) (nodeid & NODE_MASK) - -/* Can be used to consistently print a node/bus ID. */ -#define NODE_BUS_FMT "%d-%02d:%04d" -#define NODE_BUS_ARGS(__host, __nodeid) \ - __host->id, NODEID_TO_NODE(__nodeid), NODEID_TO_BUS(__nodeid) - -#define HPSB_PRINT(level, fmt, args...) \ - printk(level "ieee1394: " fmt "\n" , ## args) - -#define HPSB_DEBUG(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args) -#define HPSB_INFO(fmt, args...) HPSB_PRINT(KERN_INFO, fmt , ## args) -#define HPSB_NOTICE(fmt, args...) HPSB_PRINT(KERN_NOTICE, fmt , ## args) -#define HPSB_WARN(fmt, args...) HPSB_PRINT(KERN_WARNING, fmt , ## args) -#define HPSB_ERR(fmt, args...) HPSB_PRINT(KERN_ERR, fmt , ## args) - -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG -#define HPSB_VERBOSE(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args) -#define HPSB_DEBUG_TLABELS -#else -#define HPSB_VERBOSE(fmt, args...) do {} while (0) -#endif - -#ifdef __BIG_ENDIAN - -static inline void *memcpy_le32(u32 *dest, const u32 *__src, size_t count) -{ - void *tmp = dest; - u32 *src = (u32 *)__src; - - count /= 4; - while (count--) - *dest++ = swab32p(src++); - return tmp; -} - -#else - -static __inline__ void *memcpy_le32(u32 *dest, const u32 *src, size_t count) -{ - return memcpy(dest, src, count); -} - -#endif /* __BIG_ENDIAN */ - -#endif /* _IEEE1394_TYPES_H */ diff --git a/drivers/ieee1394/iso.c b/drivers/ieee1394/iso.c deleted file mode 100644 index 1cf6487b65ba..000000000000 --- a/drivers/ieee1394/iso.c +++ /dev/null @@ -1,568 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * kernel ISO transmission/reception - * - * Copyright (C) 2002 Maas Digital LLC - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#include -#include -#include -#include - -#include "hosts.h" -#include "iso.h" - -/** - * hpsb_iso_stop - stop DMA - */ -void hpsb_iso_stop(struct hpsb_iso *iso) -{ - if (!(iso->flags & HPSB_ISO_DRIVER_STARTED)) - return; - - iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ? - XMIT_STOP : RECV_STOP, 0); - iso->flags &= ~HPSB_ISO_DRIVER_STARTED; -} - -/** - * hpsb_iso_shutdown - deallocate buffer and DMA context - */ -void hpsb_iso_shutdown(struct hpsb_iso *iso) -{ - if (iso->flags & HPSB_ISO_DRIVER_INIT) { - hpsb_iso_stop(iso); - iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ? - XMIT_SHUTDOWN : RECV_SHUTDOWN, 0); - iso->flags &= ~HPSB_ISO_DRIVER_INIT; - } - - dma_region_free(&iso->data_buf); - kfree(iso); -} - -static struct hpsb_iso *hpsb_iso_common_init(struct hpsb_host *host, - enum hpsb_iso_type type, - unsigned int data_buf_size, - unsigned int buf_packets, - int channel, int dma_mode, - int irq_interval, - void (*callback) (struct hpsb_iso - *)) -{ - struct hpsb_iso *iso; - int dma_direction; - - /* make sure driver supports the ISO API */ - if (!host->driver->isoctl) { - printk(KERN_INFO - "ieee1394: host driver '%s' does not support the rawiso API\n", - host->driver->name); - return NULL; - } - - /* sanitize parameters */ - - if (buf_packets < 2) - buf_packets = 2; - - if ((dma_mode < HPSB_ISO_DMA_DEFAULT) - || (dma_mode > HPSB_ISO_DMA_PACKET_PER_BUFFER)) - dma_mode = HPSB_ISO_DMA_DEFAULT; - - if ((irq_interval < 0) || (irq_interval > buf_packets / 4)) - irq_interval = buf_packets / 4; - if (irq_interval == 0) /* really interrupt for each packet */ - irq_interval = 1; - - if (channel < -1 || channel >= 64) - return NULL; - - /* channel = -1 is OK for multi-channel recv but not for xmit */ - if (type == HPSB_ISO_XMIT && channel < 0) - return NULL; - - /* allocate and write the struct hpsb_iso */ - - iso = - kmalloc(sizeof(*iso) + - buf_packets * sizeof(struct hpsb_iso_packet_info), - GFP_KERNEL); - if (!iso) - return NULL; - - iso->infos = (struct hpsb_iso_packet_info *)(iso + 1); - - iso->type = type; - iso->host = host; - iso->hostdata = NULL; - iso->callback = callback; - init_waitqueue_head(&iso->waitq); - iso->channel = channel; - iso->irq_interval = irq_interval; - iso->dma_mode = dma_mode; - dma_region_init(&iso->data_buf); - iso->buf_size = PAGE_ALIGN(data_buf_size); - iso->buf_packets = buf_packets; - iso->pkt_dma = 0; - iso->first_packet = 0; - spin_lock_init(&iso->lock); - - if (iso->type == HPSB_ISO_XMIT) { - iso->n_ready_packets = iso->buf_packets; - dma_direction = PCI_DMA_TODEVICE; - } else { - iso->n_ready_packets = 0; - dma_direction = PCI_DMA_FROMDEVICE; - } - - atomic_set(&iso->overflows, 0); - iso->bytes_discarded = 0; - iso->flags = 0; - iso->prebuffer = 0; - - /* allocate the packet buffer */ - if (dma_region_alloc - (&iso->data_buf, iso->buf_size, host->pdev, dma_direction)) - goto err; - - return iso; - - err: - hpsb_iso_shutdown(iso); - return NULL; -} - -/** - * hpsb_iso_n_ready - returns number of packets ready to send or receive - */ -int hpsb_iso_n_ready(struct hpsb_iso *iso) -{ - unsigned long flags; - int val; - - spin_lock_irqsave(&iso->lock, flags); - val = iso->n_ready_packets; - spin_unlock_irqrestore(&iso->lock, flags); - - return val; -} - -/** - * hpsb_iso_xmit_init - allocate the buffer and DMA context - */ -struct hpsb_iso *hpsb_iso_xmit_init(struct hpsb_host *host, - unsigned int data_buf_size, - unsigned int buf_packets, - int channel, - int speed, - int irq_interval, - void (*callback) (struct hpsb_iso *)) -{ - struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_XMIT, - data_buf_size, buf_packets, - channel, - HPSB_ISO_DMA_DEFAULT, - irq_interval, callback); - if (!iso) - return NULL; - - iso->speed = speed; - - /* tell the driver to start working */ - if (host->driver->isoctl(iso, XMIT_INIT, 0)) - goto err; - - iso->flags |= HPSB_ISO_DRIVER_INIT; - return iso; - - err: - hpsb_iso_shutdown(iso); - return NULL; -} - -/** - * hpsb_iso_recv_init - allocate the buffer and DMA context - * - * Note, if channel = -1, multi-channel receive is enabled. - */ -struct hpsb_iso *hpsb_iso_recv_init(struct hpsb_host *host, - unsigned int data_buf_size, - unsigned int buf_packets, - int channel, - int dma_mode, - int irq_interval, - void (*callback) (struct hpsb_iso *)) -{ - struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_RECV, - data_buf_size, buf_packets, - channel, dma_mode, - irq_interval, callback); - if (!iso) - return NULL; - - /* tell the driver to start working */ - if (host->driver->isoctl(iso, RECV_INIT, 0)) - goto err; - - iso->flags |= HPSB_ISO_DRIVER_INIT; - return iso; - - err: - hpsb_iso_shutdown(iso); - return NULL; -} - -/** - * hpsb_iso_recv_listen_channel - * - * multi-channel only - */ -int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel) -{ - if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64) - return -EINVAL; - return iso->host->driver->isoctl(iso, RECV_LISTEN_CHANNEL, channel); -} - -/** - * hpsb_iso_recv_unlisten_channel - * - * multi-channel only - */ -int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel) -{ - if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64) - return -EINVAL; - return iso->host->driver->isoctl(iso, RECV_UNLISTEN_CHANNEL, channel); -} - -/** - * hpsb_iso_recv_set_channel_mask - * - * multi-channel only - */ -int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask) -{ - if (iso->type != HPSB_ISO_RECV || iso->channel != -1) - return -EINVAL; - return iso->host->driver->isoctl(iso, RECV_SET_CHANNEL_MASK, - (unsigned long)&mask); -} - -/** - * hpsb_iso_recv_flush - check for arrival of new packets - * - * check for arrival of new packets immediately (even if irq_interval - * has not yet been reached) - */ -int hpsb_iso_recv_flush(struct hpsb_iso *iso) -{ - if (iso->type != HPSB_ISO_RECV) - return -EINVAL; - return iso->host->driver->isoctl(iso, RECV_FLUSH, 0); -} - -static int do_iso_xmit_start(struct hpsb_iso *iso, int cycle) -{ - int retval = iso->host->driver->isoctl(iso, XMIT_START, cycle); - if (retval) - return retval; - - iso->flags |= HPSB_ISO_DRIVER_STARTED; - return retval; -} - -/** - * hpsb_iso_xmit_start - start DMA - */ -int hpsb_iso_xmit_start(struct hpsb_iso *iso, int cycle, int prebuffer) -{ - if (iso->type != HPSB_ISO_XMIT) - return -1; - - if (iso->flags & HPSB_ISO_DRIVER_STARTED) - return 0; - - if (cycle < -1) - cycle = -1; - else if (cycle >= 8000) - cycle %= 8000; - - iso->xmit_cycle = cycle; - - if (prebuffer < 0) - prebuffer = iso->buf_packets - 1; - else if (prebuffer == 0) - prebuffer = 1; - - if (prebuffer >= iso->buf_packets) - prebuffer = iso->buf_packets - 1; - - iso->prebuffer = prebuffer; - - /* remember the starting cycle; DMA will commence from xmit_queue_packets() - once enough packets have been buffered */ - iso->start_cycle = cycle; - - return 0; -} - -/** - * hpsb_iso_recv_start - start DMA - */ -int hpsb_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync) -{ - int retval = 0; - int isoctl_args[3]; - - if (iso->type != HPSB_ISO_RECV) - return -1; - - if (iso->flags & HPSB_ISO_DRIVER_STARTED) - return 0; - - if (cycle < -1) - cycle = -1; - else if (cycle >= 8000) - cycle %= 8000; - - isoctl_args[0] = cycle; - - if (tag_mask < 0) - /* match all tags */ - tag_mask = 0xF; - isoctl_args[1] = tag_mask; - - isoctl_args[2] = sync; - - retval = - iso->host->driver->isoctl(iso, RECV_START, - (unsigned long)&isoctl_args[0]); - if (retval) - return retval; - - iso->flags |= HPSB_ISO_DRIVER_STARTED; - return retval; -} - -/* check to make sure the user has not supplied bogus values of offset/len - * that would cause the kernel to access memory outside the buffer */ -static int hpsb_iso_check_offset_len(struct hpsb_iso *iso, - unsigned int offset, unsigned short len, - unsigned int *out_offset, - unsigned short *out_len) -{ - if (offset >= iso->buf_size) - return -EFAULT; - - /* make sure the packet does not go beyond the end of the buffer */ - if (offset + len > iso->buf_size) - return -EFAULT; - - /* check for wrap-around */ - if (offset + len < offset) - return -EFAULT; - - /* now we can trust 'offset' and 'length' */ - *out_offset = offset; - *out_len = len; - - return 0; -} - -/** - * hpsb_iso_xmit_queue_packet - queue a packet for transmission. - * - * @offset is relative to the beginning of the DMA buffer, where the packet's - * data payload should already have been placed. - */ -int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, - u8 tag, u8 sy) -{ - struct hpsb_iso_packet_info *info; - unsigned long flags; - int rv; - - if (iso->type != HPSB_ISO_XMIT) - return -EINVAL; - - /* is there space in the buffer? */ - if (iso->n_ready_packets <= 0) { - return -EBUSY; - } - - info = &iso->infos[iso->first_packet]; - - /* check for bogus offset/length */ - if (hpsb_iso_check_offset_len - (iso, offset, len, &info->offset, &info->len)) - return -EFAULT; - - info->tag = tag; - info->sy = sy; - - spin_lock_irqsave(&iso->lock, flags); - - rv = iso->host->driver->isoctl(iso, XMIT_QUEUE, (unsigned long)info); - if (rv) - goto out; - - /* increment cursors */ - iso->first_packet = (iso->first_packet + 1) % iso->buf_packets; - iso->xmit_cycle = (iso->xmit_cycle + 1) % 8000; - iso->n_ready_packets--; - - if (iso->prebuffer != 0) { - iso->prebuffer--; - if (iso->prebuffer <= 0) { - iso->prebuffer = 0; - rv = do_iso_xmit_start(iso, iso->start_cycle); - } - } - - out: - spin_unlock_irqrestore(&iso->lock, flags); - return rv; -} - -/** - * hpsb_iso_xmit_sync - wait until all queued packets have been transmitted - */ -int hpsb_iso_xmit_sync(struct hpsb_iso *iso) -{ - if (iso->type != HPSB_ISO_XMIT) - return -EINVAL; - - return wait_event_interruptible(iso->waitq, - hpsb_iso_n_ready(iso) == - iso->buf_packets); -} - -/** - * hpsb_iso_packet_sent - * - * Available to low-level drivers. - * - * Call after a packet has been transmitted to the bus (interrupt context is - * OK). @cycle is the _exact_ cycle the packet was sent on. @error should be - * non-zero if some sort of error occurred when sending the packet. - */ -void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error) -{ - unsigned long flags; - spin_lock_irqsave(&iso->lock, flags); - - /* predict the cycle of the next packet to be queued */ - - /* jump ahead by the number of packets that are already buffered */ - cycle += iso->buf_packets - iso->n_ready_packets; - cycle %= 8000; - - iso->xmit_cycle = cycle; - iso->n_ready_packets++; - iso->pkt_dma = (iso->pkt_dma + 1) % iso->buf_packets; - - if (iso->n_ready_packets == iso->buf_packets || error != 0) { - /* the buffer has run empty! */ - atomic_inc(&iso->overflows); - } - - spin_unlock_irqrestore(&iso->lock, flags); -} - -/** - * hpsb_iso_packet_received - * - * Available to low-level drivers. - * - * Call after a packet has been received (interrupt context is OK). - */ -void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len, - u16 total_len, u16 cycle, u8 channel, u8 tag, - u8 sy) -{ - unsigned long flags; - spin_lock_irqsave(&iso->lock, flags); - - if (iso->n_ready_packets == iso->buf_packets) { - /* overflow! */ - atomic_inc(&iso->overflows); - /* Record size of this discarded packet */ - iso->bytes_discarded += total_len; - } else { - struct hpsb_iso_packet_info *info = &iso->infos[iso->pkt_dma]; - info->offset = offset; - info->len = len; - info->total_len = total_len; - info->cycle = cycle; - info->channel = channel; - info->tag = tag; - info->sy = sy; - - iso->pkt_dma = (iso->pkt_dma + 1) % iso->buf_packets; - iso->n_ready_packets++; - } - - spin_unlock_irqrestore(&iso->lock, flags); -} - -/** - * hpsb_iso_recv_release_packets - release packets, reuse buffer - * - * @n_packets have been read out of the buffer, re-use the buffer space - */ -int hpsb_iso_recv_release_packets(struct hpsb_iso *iso, unsigned int n_packets) -{ - unsigned long flags; - unsigned int i; - int rv = 0; - - if (iso->type != HPSB_ISO_RECV) - return -1; - - spin_lock_irqsave(&iso->lock, flags); - for (i = 0; i < n_packets; i++) { - rv = iso->host->driver->isoctl(iso, RECV_RELEASE, - (unsigned long)&iso->infos[iso-> - first_packet]); - if (rv) - break; - - iso->first_packet = (iso->first_packet + 1) % iso->buf_packets; - iso->n_ready_packets--; - - /* release memory from packets discarded when queue was full */ - if (iso->n_ready_packets == 0) { /* Release only after all prior packets handled */ - if (iso->bytes_discarded != 0) { - struct hpsb_iso_packet_info inf; - inf.total_len = iso->bytes_discarded; - iso->host->driver->isoctl(iso, RECV_RELEASE, - (unsigned long)&inf); - iso->bytes_discarded = 0; - } - } - } - spin_unlock_irqrestore(&iso->lock, flags); - return rv; -} - -/** - * hpsb_iso_wake - * - * Available to low-level drivers. - * - * Call to wake waiting processes after buffer space has opened up. - */ -void hpsb_iso_wake(struct hpsb_iso *iso) -{ - wake_up_interruptible(&iso->waitq); - - if (iso->callback) - iso->callback(iso); -} diff --git a/drivers/ieee1394/iso.h b/drivers/ieee1394/iso.h deleted file mode 100644 index c2089c093aa7..000000000000 --- a/drivers/ieee1394/iso.h +++ /dev/null @@ -1,195 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * kernel ISO transmission/reception - * - * Copyright (C) 2002 Maas Digital LLC - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#ifndef IEEE1394_ISO_H -#define IEEE1394_ISO_H - -#include -#include -#include -#include - -#include "dma.h" - -struct hpsb_host; - -/* high-level ISO interface */ - -/* - * This API sends and receives isochronous packets on a large, - * virtually-contiguous kernel memory buffer. The buffer may be mapped - * into a user-space process for zero-copy transmission and reception. - * - * There are no explicit boundaries between packets in the buffer. A - * packet may be transmitted or received at any location. However, - * low-level drivers may impose certain restrictions on alignment or - * size of packets. (e.g. in OHCI no packet may cross a page boundary, - * and packets should be quadlet-aligned) - */ - -/* Packet descriptor - the API maintains a ring buffer of these packet - * descriptors in kernel memory (hpsb_iso.infos[]). */ -struct hpsb_iso_packet_info { - /* offset of data payload relative to the first byte of the buffer */ - __u32 offset; - - /* length of the data payload, in bytes (not including the isochronous - * header) */ - __u16 len; - - /* (recv only) the cycle number (mod 8000) on which the packet was - * received */ - __u16 cycle; - - /* (recv only) channel on which the packet was received */ - __u8 channel; - - /* 2-bit 'tag' and 4-bit 'sy' fields of the isochronous header */ - __u8 tag; - __u8 sy; - - /* length in bytes of the packet including header/trailer. - * MUST be at structure end, since the first part of this structure is - * also defined in raw1394.h (i.e. struct raw1394_iso_packet_info), is - * copied to userspace and is accessed there through libraw1394. */ - __u16 total_len; -}; - -enum hpsb_iso_type { HPSB_ISO_RECV = 0, HPSB_ISO_XMIT = 1 }; - -/* The mode of the dma when receiving iso data. Must be supported by chip */ -enum raw1394_iso_dma_recv_mode { - HPSB_ISO_DMA_DEFAULT = -1, - HPSB_ISO_DMA_OLD_ABI = 0, - HPSB_ISO_DMA_BUFFERFILL = 1, - HPSB_ISO_DMA_PACKET_PER_BUFFER = 2 -}; - -struct hpsb_iso { - enum hpsb_iso_type type; - - /* pointer to low-level driver and its private data */ - struct hpsb_host *host; - void *hostdata; - - /* a function to be called (from interrupt context) after - * outgoing packets have been sent, or incoming packets have - * arrived */ - void (*callback)(struct hpsb_iso*); - - /* wait for buffer space */ - wait_queue_head_t waitq; - - int speed; /* IEEE1394_SPEED_100, 200, or 400 */ - int channel; /* -1 if multichannel */ - int dma_mode; /* dma receive mode */ - - - /* greatest # of packets between interrupts - controls - * the maximum latency of the buffer */ - int irq_interval; - - /* the buffer for packet data payloads */ - struct dma_region data_buf; - - /* size of data_buf, in bytes (always a multiple of PAGE_SIZE) */ - unsigned int buf_size; - - /* # of packets in the ringbuffer */ - unsigned int buf_packets; - - /* protects packet cursors */ - spinlock_t lock; - - /* the index of the next packet that will be produced - or consumed by the user */ - int first_packet; - - /* the index of the next packet that will be transmitted - or received by the 1394 hardware */ - int pkt_dma; - - /* how many packets, starting at first_packet: - * (transmit) are ready to be filled with data - * (receive) contain received data */ - int n_ready_packets; - - /* how many times the buffer has overflowed or underflowed */ - atomic_t overflows; - /* how many cycles were skipped for a given context */ - atomic_t skips; - - /* Current number of bytes lost in discarded packets */ - int bytes_discarded; - - /* private flags to track initialization progress */ -#define HPSB_ISO_DRIVER_INIT (1<<0) -#define HPSB_ISO_DRIVER_STARTED (1<<1) - unsigned int flags; - - /* # of packets left to prebuffer (xmit only) */ - int prebuffer; - - /* starting cycle for DMA (xmit only) */ - int start_cycle; - - /* cycle at which next packet will be transmitted, - * -1 if not known */ - int xmit_cycle; - - /* ringbuffer of packet descriptors in regular kernel memory - * XXX Keep this last, since we use over-allocated memory from - * this entry to fill this field. */ - struct hpsb_iso_packet_info *infos; -}; - -/* functions available to high-level drivers (e.g. raw1394) */ - -struct hpsb_iso* hpsb_iso_xmit_init(struct hpsb_host *host, - unsigned int data_buf_size, - unsigned int buf_packets, - int channel, - int speed, - int irq_interval, - void (*callback)(struct hpsb_iso*)); -struct hpsb_iso* hpsb_iso_recv_init(struct hpsb_host *host, - unsigned int data_buf_size, - unsigned int buf_packets, - int channel, - int dma_mode, - int irq_interval, - void (*callback)(struct hpsb_iso*)); -int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel); -int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel); -int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask); -int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle, - int prebuffer); -int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle, - int tag_mask, int sync); -void hpsb_iso_stop(struct hpsb_iso *iso); -void hpsb_iso_shutdown(struct hpsb_iso *iso); -int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, - u8 tag, u8 sy); -int hpsb_iso_xmit_sync(struct hpsb_iso *iso); -int hpsb_iso_recv_release_packets(struct hpsb_iso *recv, - unsigned int n_packets); -int hpsb_iso_recv_flush(struct hpsb_iso *iso); -int hpsb_iso_n_ready(struct hpsb_iso *iso); - -/* the following are callbacks available to low-level drivers */ - -void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error); -void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len, - u16 total_len, u16 cycle, u8 channel, u8 tag, - u8 sy); -void hpsb_iso_wake(struct hpsb_iso *iso); - -#endif /* IEEE1394_ISO_H */ diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c deleted file mode 100644 index 18350213479e..000000000000 --- a/drivers/ieee1394/nodemgr.c +++ /dev/null @@ -1,1901 +0,0 @@ -/* - * Node information (ConfigROM) collection and management. - * - * Copyright (C) 2000 Andreas E. Bombe - * 2001-2003 Ben Collins - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "csr.h" -#include "highlevel.h" -#include "hosts.h" -#include "ieee1394.h" -#include "ieee1394_core.h" -#include "ieee1394_hotplug.h" -#include "ieee1394_types.h" -#include "ieee1394_transactions.h" -#include "nodemgr.h" - -static int ignore_drivers; -module_param(ignore_drivers, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(ignore_drivers, "Disable automatic probing for drivers."); - -struct nodemgr_csr_info { - struct hpsb_host *host; - nodeid_t nodeid; - unsigned int generation; - - kmemcheck_bitfield_begin(flags); - unsigned int speed_unverified:1; - kmemcheck_bitfield_end(flags); -}; - - -/* - * Correct the speed map entry. This is necessary - * - for nodes with link speed < phy speed, - * - for 1394b nodes with negotiated phy port speed < IEEE1394_SPEED_MAX. - * A possible speed is determined by trial and error, using quadlet reads. - */ -static int nodemgr_check_speed(struct nodemgr_csr_info *ci, u64 addr, - quadlet_t *buffer) -{ - quadlet_t q; - u8 i, *speed, old_speed, good_speed; - int error; - - speed = &(ci->host->speed[NODEID_TO_NODE(ci->nodeid)]); - old_speed = *speed; - good_speed = IEEE1394_SPEED_MAX + 1; - - /* Try every speed from S100 to old_speed. - * If we did it the other way around, a too low speed could be caught - * if the retry succeeded for some other reason, e.g. because the link - * just finished its initialization. */ - for (i = IEEE1394_SPEED_100; i <= old_speed; i++) { - *speed = i; - error = hpsb_read(ci->host, ci->nodeid, ci->generation, addr, - &q, 4); - if (error) - break; - *buffer = q; - good_speed = i; - } - if (good_speed <= IEEE1394_SPEED_MAX) { - HPSB_DEBUG("Speed probe of node " NODE_BUS_FMT " yields %s", - NODE_BUS_ARGS(ci->host, ci->nodeid), - hpsb_speedto_str[good_speed]); - *speed = good_speed; - ci->speed_unverified = 0; - return 0; - } - *speed = old_speed; - return error; -} - -static int nodemgr_bus_read(struct csr1212_csr *csr, u64 addr, - void *buffer, void *__ci) -{ - struct nodemgr_csr_info *ci = (struct nodemgr_csr_info*)__ci; - int i, error; - - for (i = 1; ; i++) { - error = hpsb_read(ci->host, ci->nodeid, ci->generation, addr, - buffer, 4); - if (!error) { - ci->speed_unverified = 0; - break; - } - /* Give up after 3rd failure. */ - if (i == 3) - break; - - /* The ieee1394_core guessed the node's speed capability from - * the self ID. Check whether a lower speed works. */ - if (ci->speed_unverified) { - error = nodemgr_check_speed(ci, addr, buffer); - if (!error) - break; - } - if (msleep_interruptible(334)) - return -EINTR; - } - return error; -} - -static struct csr1212_bus_ops nodemgr_csr_ops = { - .bus_read = nodemgr_bus_read, -}; - - -/* - * Basically what we do here is start off retrieving the bus_info block. - * From there will fill in some info about the node, verify it is of IEEE - * 1394 type, and that the crc checks out ok. After that we start off with - * the root directory, and subdirectories. To do this, we retrieve the - * quadlet header for a directory, find out the length, and retrieve the - * complete directory entry (be it a leaf or a directory). We then process - * it and add the info to our structure for that particular node. - * - * We verify CRC's along the way for each directory/block/leaf. The entire - * node structure is generic, and simply stores the information in a way - * that's easy to parse by the protocol interface. - */ - -/* - * The nodemgr relies heavily on the Driver Model for device callbacks and - * driver/device mappings. The old nodemgr used to handle all this itself, - * but now we are much simpler because of the LDM. - */ - -struct host_info { - struct hpsb_host *host; - struct list_head list; - struct task_struct *thread; -}; - -static int nodemgr_bus_match(struct device * dev, struct device_driver * drv); -static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env); - -struct bus_type ieee1394_bus_type = { - .name = "ieee1394", - .match = nodemgr_bus_match, -}; - -static void host_cls_release(struct device *dev) -{ - put_device(&container_of((dev), struct hpsb_host, host_dev)->device); -} - -struct class hpsb_host_class = { - .name = "ieee1394_host", - .dev_release = host_cls_release, -}; - -static void ne_cls_release(struct device *dev) -{ - put_device(&container_of((dev), struct node_entry, node_dev)->device); -} - -static struct class nodemgr_ne_class = { - .name = "ieee1394_node", - .dev_release = ne_cls_release, -}; - -static void ud_cls_release(struct device *dev) -{ - put_device(&container_of((dev), struct unit_directory, unit_dev)->device); -} - -/* The name here is only so that unit directory hotplug works with old - * style hotplug, which only ever did unit directories anyway. - */ -static struct class nodemgr_ud_class = { - .name = "ieee1394", - .dev_release = ud_cls_release, - .dev_uevent = nodemgr_uevent, -}; - -static struct hpsb_highlevel nodemgr_highlevel; - - -static void nodemgr_release_ud(struct device *dev) -{ - struct unit_directory *ud = container_of(dev, struct unit_directory, device); - - if (ud->vendor_name_kv) - csr1212_release_keyval(ud->vendor_name_kv); - if (ud->model_name_kv) - csr1212_release_keyval(ud->model_name_kv); - - kfree(ud); -} - -static void nodemgr_release_ne(struct device *dev) -{ - struct node_entry *ne = container_of(dev, struct node_entry, device); - - if (ne->vendor_name_kv) - csr1212_release_keyval(ne->vendor_name_kv); - - kfree(ne); -} - - -static void nodemgr_release_host(struct device *dev) -{ - struct hpsb_host *host = container_of(dev, struct hpsb_host, device); - - csr1212_destroy_csr(host->csr.rom); - - kfree(host); -} - -static int nodemgr_ud_platform_data; - -static struct device nodemgr_dev_template_ud = { - .bus = &ieee1394_bus_type, - .release = nodemgr_release_ud, - .platform_data = &nodemgr_ud_platform_data, -}; - -static struct device nodemgr_dev_template_ne = { - .bus = &ieee1394_bus_type, - .release = nodemgr_release_ne, -}; - -/* This dummy driver prevents the host devices from being scanned. We have no - * useful drivers for them yet, and there would be a deadlock possible if the - * driver core scans the host device while the host's low-level driver (i.e. - * the host's parent device) is being removed. */ -static struct device_driver nodemgr_mid_layer_driver = { - .bus = &ieee1394_bus_type, - .name = "nodemgr", - .owner = THIS_MODULE, -}; - -struct device nodemgr_dev_template_host = { - .bus = &ieee1394_bus_type, - .release = nodemgr_release_host, -}; - - -#define fw_attr(class, class_type, field, type, format_string) \ -static ssize_t fw_show_##class##_##field (struct device *dev, struct device_attribute *attr, char *buf)\ -{ \ - class_type *class; \ - class = container_of(dev, class_type, device); \ - return sprintf(buf, format_string, (type)class->field); \ -} \ -static struct device_attribute dev_attr_##class##_##field = { \ - .attr = {.name = __stringify(field), .mode = S_IRUGO }, \ - .show = fw_show_##class##_##field, \ -}; - -#define fw_attr_td(class, class_type, td_kv) \ -static ssize_t fw_show_##class##_##td_kv (struct device *dev, struct device_attribute *attr, char *buf)\ -{ \ - int len; \ - class_type *class = container_of(dev, class_type, device); \ - len = (class->td_kv->value.leaf.len - 2) * sizeof(quadlet_t); \ - memcpy(buf, \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(class->td_kv), \ - len); \ - while (buf[len - 1] == '\0') \ - len--; \ - buf[len++] = '\n'; \ - buf[len] = '\0'; \ - return len; \ -} \ -static struct device_attribute dev_attr_##class##_##td_kv = { \ - .attr = {.name = __stringify(td_kv), .mode = S_IRUGO }, \ - .show = fw_show_##class##_##td_kv, \ -}; - - -#define fw_drv_attr(field, type, format_string) \ -static ssize_t fw_drv_show_##field (struct device_driver *drv, char *buf) \ -{ \ - struct hpsb_protocol_driver *driver; \ - driver = container_of(drv, struct hpsb_protocol_driver, driver); \ - return sprintf(buf, format_string, (type)driver->field);\ -} \ -static struct driver_attribute driver_attr_drv_##field = { \ - .attr = {.name = __stringify(field), .mode = S_IRUGO }, \ - .show = fw_drv_show_##field, \ -}; - - -static ssize_t fw_show_ne_bus_options(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct node_entry *ne = container_of(dev, struct node_entry, device); - - return sprintf(buf, "IRMC(%d) CMC(%d) ISC(%d) BMC(%d) PMC(%d) GEN(%d) " - "LSPD(%d) MAX_REC(%d) MAX_ROM(%d) CYC_CLK_ACC(%d)\n", - ne->busopt.irmc, - ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc, - ne->busopt.pmc, ne->busopt.generation, ne->busopt.lnkspd, - ne->busopt.max_rec, - ne->busopt.max_rom, - ne->busopt.cyc_clk_acc); -} -static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL); - - -#ifdef HPSB_DEBUG_TLABELS -static ssize_t fw_show_ne_tlabels_free(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct node_entry *ne = container_of(dev, struct node_entry, device); - unsigned long flags; - unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map; - int tf; - - spin_lock_irqsave(&hpsb_tlabel_lock, flags); - tf = 64 - bitmap_weight(tp, 64); - spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); - - return sprintf(buf, "%d\n", tf); -} -static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL); - - -static ssize_t fw_show_ne_tlabels_mask(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct node_entry *ne = container_of(dev, struct node_entry, device); - unsigned long flags; - unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map; - u64 tm; - - spin_lock_irqsave(&hpsb_tlabel_lock, flags); -#if (BITS_PER_LONG <= 32) - tm = ((u64)tp[0] << 32) + tp[1]; -#else - tm = tp[0]; -#endif - spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); - - return sprintf(buf, "0x%016llx\n", (unsigned long long)tm); -} -static DEVICE_ATTR(tlabels_mask, S_IRUGO, fw_show_ne_tlabels_mask, NULL); -#endif /* HPSB_DEBUG_TLABELS */ - - -static ssize_t fw_set_ignore_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct unit_directory *ud = container_of(dev, struct unit_directory, device); - int state = simple_strtoul(buf, NULL, 10); - - if (state == 1) { - ud->ignore_driver = 1; - device_release_driver(dev); - } else if (state == 0) - ud->ignore_driver = 0; - - return count; -} -static ssize_t fw_get_ignore_driver(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct unit_directory *ud = container_of(dev, struct unit_directory, device); - - return sprintf(buf, "%d\n", ud->ignore_driver); -} -static DEVICE_ATTR(ignore_driver, S_IWUSR | S_IRUGO, fw_get_ignore_driver, fw_set_ignore_driver); - - -static ssize_t fw_set_rescan(struct bus_type *bus, const char *buf, - size_t count) -{ - int error = 0; - - if (simple_strtoul(buf, NULL, 10) == 1) - error = bus_rescan_devices(&ieee1394_bus_type); - return error ? error : count; -} -static ssize_t fw_get_rescan(struct bus_type *bus, char *buf) -{ - return sprintf(buf, "You can force a rescan of the bus for " - "drivers by writing a 1 to this file\n"); -} -static BUS_ATTR(rescan, S_IWUSR | S_IRUGO, fw_get_rescan, fw_set_rescan); - - -static ssize_t fw_set_ignore_drivers(struct bus_type *bus, const char *buf, size_t count) -{ - int state = simple_strtoul(buf, NULL, 10); - - if (state == 1) - ignore_drivers = 1; - else if (state == 0) - ignore_drivers = 0; - - return count; -} -static ssize_t fw_get_ignore_drivers(struct bus_type *bus, char *buf) -{ - return sprintf(buf, "%d\n", ignore_drivers); -} -static BUS_ATTR(ignore_drivers, S_IWUSR | S_IRUGO, fw_get_ignore_drivers, fw_set_ignore_drivers); - - -struct bus_attribute *const fw_bus_attrs[] = { - &bus_attr_rescan, - &bus_attr_ignore_drivers, - NULL -}; - - -fw_attr(ne, struct node_entry, capabilities, unsigned int, "0x%06x\n") -fw_attr(ne, struct node_entry, nodeid, unsigned int, "0x%04x\n") - -fw_attr(ne, struct node_entry, vendor_id, unsigned int, "0x%06x\n") -fw_attr_td(ne, struct node_entry, vendor_name_kv) - -fw_attr(ne, struct node_entry, guid, unsigned long long, "0x%016Lx\n") -fw_attr(ne, struct node_entry, guid_vendor_id, unsigned int, "0x%06x\n") -fw_attr(ne, struct node_entry, in_limbo, int, "%d\n"); - -static struct device_attribute *const fw_ne_attrs[] = { - &dev_attr_ne_guid, - &dev_attr_ne_guid_vendor_id, - &dev_attr_ne_capabilities, - &dev_attr_ne_vendor_id, - &dev_attr_ne_nodeid, - &dev_attr_bus_options, -#ifdef HPSB_DEBUG_TLABELS - &dev_attr_tlabels_free, - &dev_attr_tlabels_mask, -#endif -}; - - - -fw_attr(ud, struct unit_directory, address, unsigned long long, "0x%016Lx\n") -fw_attr(ud, struct unit_directory, length, int, "%d\n") -/* These are all dependent on the value being provided */ -fw_attr(ud, struct unit_directory, vendor_id, unsigned int, "0x%06x\n") -fw_attr(ud, struct unit_directory, model_id, unsigned int, "0x%06x\n") -fw_attr(ud, struct unit_directory, specifier_id, unsigned int, "0x%06x\n") -fw_attr(ud, struct unit_directory, version, unsigned int, "0x%06x\n") -fw_attr_td(ud, struct unit_directory, vendor_name_kv) -fw_attr_td(ud, struct unit_directory, model_name_kv) - -static struct device_attribute *const fw_ud_attrs[] = { - &dev_attr_ud_address, - &dev_attr_ud_length, - &dev_attr_ignore_driver, -}; - - -fw_attr(host, struct hpsb_host, node_count, int, "%d\n") -fw_attr(host, struct hpsb_host, selfid_count, int, "%d\n") -fw_attr(host, struct hpsb_host, nodes_active, int, "%d\n") -fw_attr(host, struct hpsb_host, in_bus_reset, int, "%d\n") -fw_attr(host, struct hpsb_host, is_root, int, "%d\n") -fw_attr(host, struct hpsb_host, is_cycmst, int, "%d\n") -fw_attr(host, struct hpsb_host, is_irm, int, "%d\n") -fw_attr(host, struct hpsb_host, is_busmgr, int, "%d\n") - -static struct device_attribute *const fw_host_attrs[] = { - &dev_attr_host_node_count, - &dev_attr_host_selfid_count, - &dev_attr_host_nodes_active, - &dev_attr_host_in_bus_reset, - &dev_attr_host_is_root, - &dev_attr_host_is_cycmst, - &dev_attr_host_is_irm, - &dev_attr_host_is_busmgr, -}; - - -static ssize_t fw_show_drv_device_ids(struct device_driver *drv, char *buf) -{ - struct hpsb_protocol_driver *driver; - const struct ieee1394_device_id *id; - int length = 0; - char *scratch = buf; - - driver = container_of(drv, struct hpsb_protocol_driver, driver); - id = driver->id_table; - if (!id) - return 0; - - for (; id->match_flags != 0; id++) { - int need_coma = 0; - - if (id->match_flags & IEEE1394_MATCH_VENDOR_ID) { - length += sprintf(scratch, "vendor_id=0x%06x", id->vendor_id); - scratch = buf + length; - need_coma++; - } - - if (id->match_flags & IEEE1394_MATCH_MODEL_ID) { - length += sprintf(scratch, "%smodel_id=0x%06x", - need_coma++ ? "," : "", - id->model_id); - scratch = buf + length; - } - - if (id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) { - length += sprintf(scratch, "%sspecifier_id=0x%06x", - need_coma++ ? "," : "", - id->specifier_id); - scratch = buf + length; - } - - if (id->match_flags & IEEE1394_MATCH_VERSION) { - length += sprintf(scratch, "%sversion=0x%06x", - need_coma++ ? "," : "", - id->version); - scratch = buf + length; - } - - if (need_coma) { - *scratch++ = '\n'; - length++; - } - } - - return length; -} -static DRIVER_ATTR(device_ids,S_IRUGO,fw_show_drv_device_ids,NULL); - - -fw_drv_attr(name, const char *, "%s\n") - -static struct driver_attribute *const fw_drv_attrs[] = { - &driver_attr_drv_name, - &driver_attr_device_ids, -}; - - -static void nodemgr_create_drv_files(struct hpsb_protocol_driver *driver) -{ - struct device_driver *drv = &driver->driver; - int i; - - for (i = 0; i < ARRAY_SIZE(fw_drv_attrs); i++) - if (driver_create_file(drv, fw_drv_attrs[i])) - goto fail; - return; -fail: - HPSB_ERR("Failed to add sysfs attribute"); -} - - -static void nodemgr_remove_drv_files(struct hpsb_protocol_driver *driver) -{ - struct device_driver *drv = &driver->driver; - int i; - - for (i = 0; i < ARRAY_SIZE(fw_drv_attrs); i++) - driver_remove_file(drv, fw_drv_attrs[i]); -} - - -static void nodemgr_create_ne_dev_files(struct node_entry *ne) -{ - struct device *dev = &ne->device; - int i; - - for (i = 0; i < ARRAY_SIZE(fw_ne_attrs); i++) - if (device_create_file(dev, fw_ne_attrs[i])) - goto fail; - return; -fail: - HPSB_ERR("Failed to add sysfs attribute"); -} - - -static void nodemgr_create_host_dev_files(struct hpsb_host *host) -{ - struct device *dev = &host->device; - int i; - - for (i = 0; i < ARRAY_SIZE(fw_host_attrs); i++) - if (device_create_file(dev, fw_host_attrs[i])) - goto fail; - return; -fail: - HPSB_ERR("Failed to add sysfs attribute"); -} - - -static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, - nodeid_t nodeid); - -static void nodemgr_update_host_dev_links(struct hpsb_host *host) -{ - struct device *dev = &host->device; - struct node_entry *ne; - - sysfs_remove_link(&dev->kobj, "irm_id"); - sysfs_remove_link(&dev->kobj, "busmgr_id"); - sysfs_remove_link(&dev->kobj, "host_id"); - - if ((ne = find_entry_by_nodeid(host, host->irm_id)) && - sysfs_create_link(&dev->kobj, &ne->device.kobj, "irm_id")) - goto fail; - if ((ne = find_entry_by_nodeid(host, host->busmgr_id)) && - sysfs_create_link(&dev->kobj, &ne->device.kobj, "busmgr_id")) - goto fail; - if ((ne = find_entry_by_nodeid(host, host->node_id)) && - sysfs_create_link(&dev->kobj, &ne->device.kobj, "host_id")) - goto fail; - return; -fail: - HPSB_ERR("Failed to update sysfs attributes for host %d", host->id); -} - -static void nodemgr_create_ud_dev_files(struct unit_directory *ud) -{ - struct device *dev = &ud->device; - int i; - - for (i = 0; i < ARRAY_SIZE(fw_ud_attrs); i++) - if (device_create_file(dev, fw_ud_attrs[i])) - goto fail; - if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) - if (device_create_file(dev, &dev_attr_ud_specifier_id)) - goto fail; - if (ud->flags & UNIT_DIRECTORY_VERSION) - if (device_create_file(dev, &dev_attr_ud_version)) - goto fail; - if (ud->flags & UNIT_DIRECTORY_VENDOR_ID) { - if (device_create_file(dev, &dev_attr_ud_vendor_id)) - goto fail; - if (ud->vendor_name_kv && - device_create_file(dev, &dev_attr_ud_vendor_name_kv)) - goto fail; - } - if (ud->flags & UNIT_DIRECTORY_MODEL_ID) { - if (device_create_file(dev, &dev_attr_ud_model_id)) - goto fail; - if (ud->model_name_kv && - device_create_file(dev, &dev_attr_ud_model_name_kv)) - goto fail; - } - return; -fail: - HPSB_ERR("Failed to add sysfs attribute"); -} - - -static int nodemgr_bus_match(struct device * dev, struct device_driver * drv) -{ - struct hpsb_protocol_driver *driver; - struct unit_directory *ud; - const struct ieee1394_device_id *id; - - /* We only match unit directories */ - if (dev->platform_data != &nodemgr_ud_platform_data) - return 0; - - ud = container_of(dev, struct unit_directory, device); - if (ud->ne->in_limbo || ud->ignore_driver) - return 0; - - /* We only match drivers of type hpsb_protocol_driver */ - if (drv == &nodemgr_mid_layer_driver) - return 0; - - driver = container_of(drv, struct hpsb_protocol_driver, driver); - id = driver->id_table; - if (!id) - return 0; - - for (; id->match_flags != 0; id++) { - if ((id->match_flags & IEEE1394_MATCH_VENDOR_ID) && - id->vendor_id != ud->vendor_id) - continue; - - if ((id->match_flags & IEEE1394_MATCH_MODEL_ID) && - id->model_id != ud->model_id) - continue; - - if ((id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) && - id->specifier_id != ud->specifier_id) - continue; - - if ((id->match_flags & IEEE1394_MATCH_VERSION) && - id->version != ud->version) - continue; - - return 1; - } - - return 0; -} - - -static DEFINE_MUTEX(nodemgr_serialize_remove_uds); - -static int match_ne(struct device *dev, void *data) -{ - struct unit_directory *ud; - struct node_entry *ne = data; - - ud = container_of(dev, struct unit_directory, unit_dev); - return ud->ne == ne; -} - -static void nodemgr_remove_uds(struct node_entry *ne) -{ - struct device *dev; - struct unit_directory *ud; - - /* Use class_find device to iterate the devices. Since this code - * may be called from other contexts besides the knodemgrds, - * protect it by nodemgr_serialize_remove_uds. - */ - mutex_lock(&nodemgr_serialize_remove_uds); - for (;;) { - dev = class_find_device(&nodemgr_ud_class, NULL, ne, match_ne); - if (!dev) - break; - ud = container_of(dev, struct unit_directory, unit_dev); - put_device(dev); - device_unregister(&ud->unit_dev); - device_unregister(&ud->device); - } - mutex_unlock(&nodemgr_serialize_remove_uds); -} - - -static void nodemgr_remove_ne(struct node_entry *ne) -{ - struct device *dev; - - dev = get_device(&ne->device); - if (!dev) - return; - - HPSB_DEBUG("Node removed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", - NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid); - nodemgr_remove_uds(ne); - - device_unregister(&ne->node_dev); - device_unregister(dev); - - put_device(dev); -} - -static int remove_host_dev(struct device *dev, void *data) -{ - if (dev->bus == &ieee1394_bus_type) - nodemgr_remove_ne(container_of(dev, struct node_entry, - device)); - return 0; -} - -static void nodemgr_remove_host_dev(struct device *dev) -{ - device_for_each_child(dev, NULL, remove_host_dev); - sysfs_remove_link(&dev->kobj, "irm_id"); - sysfs_remove_link(&dev->kobj, "busmgr_id"); - sysfs_remove_link(&dev->kobj, "host_id"); -} - - -static void nodemgr_update_bus_options(struct node_entry *ne) -{ -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG - static const u16 mr[] = { 4, 64, 1024, 0}; -#endif - quadlet_t busoptions = be32_to_cpu(ne->csr->bus_info_data[2]); - - ne->busopt.irmc = (busoptions >> 31) & 1; - ne->busopt.cmc = (busoptions >> 30) & 1; - ne->busopt.isc = (busoptions >> 29) & 1; - ne->busopt.bmc = (busoptions >> 28) & 1; - ne->busopt.pmc = (busoptions >> 27) & 1; - ne->busopt.cyc_clk_acc = (busoptions >> 16) & 0xff; - ne->busopt.max_rec = 1 << (((busoptions >> 12) & 0xf) + 1); - ne->busopt.max_rom = (busoptions >> 8) & 0x3; - ne->busopt.generation = (busoptions >> 4) & 0xf; - ne->busopt.lnkspd = busoptions & 0x7; - - HPSB_VERBOSE("NodeMgr: raw=0x%08x irmc=%d cmc=%d isc=%d bmc=%d pmc=%d " - "cyc_clk_acc=%d max_rec=%d max_rom=%d gen=%d lspd=%d", - busoptions, ne->busopt.irmc, ne->busopt.cmc, - ne->busopt.isc, ne->busopt.bmc, ne->busopt.pmc, - ne->busopt.cyc_clk_acc, ne->busopt.max_rec, - mr[ne->busopt.max_rom], - ne->busopt.generation, ne->busopt.lnkspd); -} - - -static struct node_entry *nodemgr_create_node(octlet_t guid, - struct csr1212_csr *csr, struct hpsb_host *host, - nodeid_t nodeid, unsigned int generation) -{ - struct node_entry *ne; - - ne = kzalloc(sizeof(*ne), GFP_KERNEL); - if (!ne) - goto fail_alloc; - - ne->host = host; - ne->nodeid = nodeid; - ne->generation = generation; - ne->needs_probe = true; - - ne->guid = guid; - ne->guid_vendor_id = (guid >> 40) & 0xffffff; - ne->csr = csr; - - memcpy(&ne->device, &nodemgr_dev_template_ne, - sizeof(ne->device)); - ne->device.parent = &host->device; - dev_set_name(&ne->device, "%016Lx", (unsigned long long)(ne->guid)); - - ne->node_dev.parent = &ne->device; - ne->node_dev.class = &nodemgr_ne_class; - dev_set_name(&ne->node_dev, "%016Lx", (unsigned long long)(ne->guid)); - - if (device_register(&ne->device)) - goto fail_devreg; - if (device_register(&ne->node_dev)) - goto fail_classdevreg; - get_device(&ne->device); - - nodemgr_create_ne_dev_files(ne); - - nodemgr_update_bus_options(ne); - - HPSB_DEBUG("%s added: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", - (host->node_id == nodeid) ? "Host" : "Node", - NODE_BUS_ARGS(host, nodeid), (unsigned long long)guid); - - return ne; - -fail_classdevreg: - device_unregister(&ne->device); -fail_devreg: - kfree(ne); -fail_alloc: - HPSB_ERR("Failed to create node ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", - NODE_BUS_ARGS(host, nodeid), (unsigned long long)guid); - - return NULL; -} - -static int match_ne_guid(struct device *dev, void *data) -{ - struct node_entry *ne; - u64 *guid = data; - - ne = container_of(dev, struct node_entry, node_dev); - return ne->guid == *guid; -} - -static struct node_entry *find_entry_by_guid(u64 guid) -{ - struct device *dev; - struct node_entry *ne; - - dev = class_find_device(&nodemgr_ne_class, NULL, &guid, match_ne_guid); - if (!dev) - return NULL; - ne = container_of(dev, struct node_entry, node_dev); - put_device(dev); - - return ne; -} - -struct match_nodeid_parameter { - struct hpsb_host *host; - nodeid_t nodeid; -}; - -static int match_ne_nodeid(struct device *dev, void *data) -{ - int found = 0; - struct node_entry *ne; - struct match_nodeid_parameter *p = data; - - if (!dev) - goto ret; - ne = container_of(dev, struct node_entry, node_dev); - if (ne->host == p->host && ne->nodeid == p->nodeid) - found = 1; -ret: - return found; -} - -static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, - nodeid_t nodeid) -{ - struct device *dev; - struct node_entry *ne; - struct match_nodeid_parameter p; - - p.host = host; - p.nodeid = nodeid; - - dev = class_find_device(&nodemgr_ne_class, NULL, &p, match_ne_nodeid); - if (!dev) - return NULL; - ne = container_of(dev, struct node_entry, node_dev); - put_device(dev); - - return ne; -} - - -static void nodemgr_register_device(struct node_entry *ne, - struct unit_directory *ud, struct device *parent) -{ - memcpy(&ud->device, &nodemgr_dev_template_ud, - sizeof(ud->device)); - - ud->device.parent = parent; - - dev_set_name(&ud->device, "%s-%u", dev_name(&ne->device), ud->id); - - ud->unit_dev.parent = &ud->device; - ud->unit_dev.class = &nodemgr_ud_class; - dev_set_name(&ud->unit_dev, "%s-%u", dev_name(&ne->device), ud->id); - - if (device_register(&ud->device)) - goto fail_devreg; - if (device_register(&ud->unit_dev)) - goto fail_classdevreg; - get_device(&ud->device); - - nodemgr_create_ud_dev_files(ud); - - return; - -fail_classdevreg: - device_unregister(&ud->device); -fail_devreg: - HPSB_ERR("Failed to create unit %s", dev_name(&ud->device)); -} - - -/* This implementation currently only scans the config rom and its - * immediate unit directories looking for software_id and - * software_version entries, in order to get driver autoloading working. */ -static struct unit_directory *nodemgr_process_unit_directory - (struct node_entry *ne, struct csr1212_keyval *ud_kv, - unsigned int *id, struct unit_directory *parent) -{ - struct unit_directory *ud; - struct unit_directory *ud_child = NULL; - struct csr1212_dentry *dentry; - struct csr1212_keyval *kv; - u8 last_key_id = 0; - - ud = kzalloc(sizeof(*ud), GFP_KERNEL); - if (!ud) - goto unit_directory_error; - - ud->ne = ne; - ud->ignore_driver = ignore_drivers; - ud->address = ud_kv->offset + CSR1212_REGISTER_SPACE_BASE; - ud->directory_id = ud->address & 0xffffff; - ud->ud_kv = ud_kv; - ud->id = (*id)++; - - /* inherit vendor_id from root directory if none exists in unit dir */ - ud->vendor_id = ne->vendor_id; - - csr1212_for_each_dir_entry(ne->csr, kv, ud_kv, dentry) { - switch (kv->key.id) { - case CSR1212_KV_ID_VENDOR: - if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) { - ud->vendor_id = kv->value.immediate; - ud->flags |= UNIT_DIRECTORY_VENDOR_ID; - } - break; - - case CSR1212_KV_ID_MODEL: - ud->model_id = kv->value.immediate; - ud->flags |= UNIT_DIRECTORY_MODEL_ID; - break; - - case CSR1212_KV_ID_SPECIFIER_ID: - ud->specifier_id = kv->value.immediate; - ud->flags |= UNIT_DIRECTORY_SPECIFIER_ID; - break; - - case CSR1212_KV_ID_VERSION: - ud->version = kv->value.immediate; - ud->flags |= UNIT_DIRECTORY_VERSION; - break; - - case CSR1212_KV_ID_DESCRIPTOR: - if (kv->key.type == CSR1212_KV_TYPE_LEAF && - CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 && - CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 && - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 && - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 && - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) { - switch (last_key_id) { - case CSR1212_KV_ID_VENDOR: - csr1212_keep_keyval(kv); - ud->vendor_name_kv = kv; - break; - - case CSR1212_KV_ID_MODEL: - csr1212_keep_keyval(kv); - ud->model_name_kv = kv; - break; - - } - } /* else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) ... */ - break; - - case CSR1212_KV_ID_DEPENDENT_INFO: - /* Logical Unit Number */ - if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) { - if (ud->flags & UNIT_DIRECTORY_HAS_LUN) { - ud_child = kmemdup(ud, sizeof(*ud_child), GFP_KERNEL); - if (!ud_child) - goto unit_directory_error; - nodemgr_register_device(ne, ud_child, &ne->device); - ud_child = NULL; - - ud->id = (*id)++; - } - ud->lun = kv->value.immediate; - ud->flags |= UNIT_DIRECTORY_HAS_LUN; - - /* Logical Unit Directory */ - } else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) { - /* This should really be done in SBP2 as this is - * doing SBP2 specific parsing. - */ - - /* first register the parent unit */ - ud->flags |= UNIT_DIRECTORY_HAS_LUN_DIRECTORY; - if (ud->device.bus != &ieee1394_bus_type) - nodemgr_register_device(ne, ud, &ne->device); - - /* process the child unit */ - ud_child = nodemgr_process_unit_directory(ne, kv, id, ud); - - if (ud_child == NULL) - break; - - /* inherit unspecified values, the driver core picks it up */ - if ((ud->flags & UNIT_DIRECTORY_MODEL_ID) && - !(ud_child->flags & UNIT_DIRECTORY_MODEL_ID)) - { - ud_child->flags |= UNIT_DIRECTORY_MODEL_ID; - ud_child->model_id = ud->model_id; - } - if ((ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) && - !(ud_child->flags & UNIT_DIRECTORY_SPECIFIER_ID)) - { - ud_child->flags |= UNIT_DIRECTORY_SPECIFIER_ID; - ud_child->specifier_id = ud->specifier_id; - } - if ((ud->flags & UNIT_DIRECTORY_VERSION) && - !(ud_child->flags & UNIT_DIRECTORY_VERSION)) - { - ud_child->flags |= UNIT_DIRECTORY_VERSION; - ud_child->version = ud->version; - } - - /* register the child unit */ - ud_child->flags |= UNIT_DIRECTORY_LUN_DIRECTORY; - nodemgr_register_device(ne, ud_child, &ud->device); - } - - break; - - case CSR1212_KV_ID_DIRECTORY_ID: - ud->directory_id = kv->value.immediate; - break; - - default: - break; - } - last_key_id = kv->key.id; - } - - /* do not process child units here and only if not already registered */ - if (!parent && ud->device.bus != &ieee1394_bus_type) - nodemgr_register_device(ne, ud, &ne->device); - - return ud; - -unit_directory_error: - kfree(ud); - return NULL; -} - - -static void nodemgr_process_root_directory(struct node_entry *ne) -{ - unsigned int ud_id = 0; - struct csr1212_dentry *dentry; - struct csr1212_keyval *kv, *vendor_name_kv = NULL; - u8 last_key_id = 0; - - ne->needs_probe = false; - - csr1212_for_each_dir_entry(ne->csr, kv, ne->csr->root_kv, dentry) { - switch (kv->key.id) { - case CSR1212_KV_ID_VENDOR: - ne->vendor_id = kv->value.immediate; - break; - - case CSR1212_KV_ID_NODE_CAPABILITIES: - ne->capabilities = kv->value.immediate; - break; - - case CSR1212_KV_ID_UNIT: - nodemgr_process_unit_directory(ne, kv, &ud_id, NULL); - break; - - case CSR1212_KV_ID_DESCRIPTOR: - if (last_key_id == CSR1212_KV_ID_VENDOR) { - if (kv->key.type == CSR1212_KV_TYPE_LEAF && - CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 && - CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 && - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 && - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 && - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) { - csr1212_keep_keyval(kv); - vendor_name_kv = kv; - } - } - break; - } - last_key_id = kv->key.id; - } - - if (ne->vendor_name_kv) { - kv = ne->vendor_name_kv; - ne->vendor_name_kv = vendor_name_kv; - csr1212_release_keyval(kv); - } else if (vendor_name_kv) { - ne->vendor_name_kv = vendor_name_kv; - if (device_create_file(&ne->device, - &dev_attr_ne_vendor_name_kv) != 0) - HPSB_ERR("Failed to add sysfs attribute"); - } -} - -#ifdef CONFIG_HOTPLUG - -static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - struct unit_directory *ud; - int retval = 0; - /* ieee1394:venNmoNspNverN */ - char buf[8 + 1 + 3 + 8 + 2 + 8 + 2 + 8 + 3 + 8 + 1]; - - if (!dev) - return -ENODEV; - - ud = container_of(dev, struct unit_directory, unit_dev); - - if (ud->ne->in_limbo || ud->ignore_driver) - return -ENODEV; - -#define PUT_ENVP(fmt,val) \ -do { \ - retval = add_uevent_var(env, fmt, val); \ - if (retval) \ - return retval; \ -} while (0) - - PUT_ENVP("VENDOR_ID=%06x", ud->vendor_id); - PUT_ENVP("MODEL_ID=%06x", ud->model_id); - PUT_ENVP("GUID=%016Lx", (unsigned long long)ud->ne->guid); - PUT_ENVP("SPECIFIER_ID=%06x", ud->specifier_id); - PUT_ENVP("VERSION=%06x", ud->version); - snprintf(buf, sizeof(buf), "ieee1394:ven%08Xmo%08Xsp%08Xver%08X", - ud->vendor_id, - ud->model_id, - ud->specifier_id, - ud->version); - PUT_ENVP("MODALIAS=%s", buf); - -#undef PUT_ENVP - - return 0; -} - -#else - -static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - return -ENODEV; -} - -#endif /* CONFIG_HOTPLUG */ - - -int __hpsb_register_protocol(struct hpsb_protocol_driver *drv, - struct module *owner) -{ - int error; - - drv->driver.bus = &ieee1394_bus_type; - drv->driver.owner = owner; - drv->driver.name = drv->name; - - /* This will cause a probe for devices */ - error = driver_register(&drv->driver); - if (!error) - nodemgr_create_drv_files(drv); - return error; -} - -void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver) -{ - nodemgr_remove_drv_files(driver); - /* This will subsequently disconnect all devices that our driver - * is attached to. */ - driver_unregister(&driver->driver); -} - - -/* - * This function updates nodes that were present on the bus before the - * reset and still are after the reset. The nodeid and the config rom - * may have changed, and the drivers managing this device must be - * informed that this device just went through a bus reset, to allow - * the to take whatever actions required. - */ -static void nodemgr_update_node(struct node_entry *ne, struct csr1212_csr *csr, - nodeid_t nodeid, unsigned int generation) -{ - if (ne->nodeid != nodeid) { - HPSB_DEBUG("Node changed: " NODE_BUS_FMT " -> " NODE_BUS_FMT, - NODE_BUS_ARGS(ne->host, ne->nodeid), - NODE_BUS_ARGS(ne->host, nodeid)); - ne->nodeid = nodeid; - } - - if (ne->busopt.generation != ((be32_to_cpu(csr->bus_info_data[2]) >> 4) & 0xf)) { - kfree(ne->csr->private); - csr1212_destroy_csr(ne->csr); - ne->csr = csr; - - /* If the node's configrom generation has changed, we - * unregister all the unit directories. */ - nodemgr_remove_uds(ne); - - nodemgr_update_bus_options(ne); - - /* Mark the node as new, so it gets re-probed */ - ne->needs_probe = true; - } else { - /* old cache is valid, so update its generation */ - struct nodemgr_csr_info *ci = ne->csr->private; - ci->generation = generation; - /* free the partially filled now unneeded new cache */ - kfree(csr->private); - csr1212_destroy_csr(csr); - } - - /* Finally, mark the node current */ - smp_wmb(); - ne->generation = generation; - - if (ne->in_limbo) { - device_remove_file(&ne->device, &dev_attr_ne_in_limbo); - ne->in_limbo = false; - - HPSB_DEBUG("Node reactivated: " - "ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", - NODE_BUS_ARGS(ne->host, ne->nodeid), - (unsigned long long)ne->guid); - } -} - -static void nodemgr_node_scan_one(struct hpsb_host *host, - nodeid_t nodeid, int generation) -{ - struct node_entry *ne; - octlet_t guid; - struct csr1212_csr *csr; - struct nodemgr_csr_info *ci; - u8 *speed; - - ci = kmalloc(sizeof(*ci), GFP_KERNEL); - kmemcheck_annotate_bitfield(ci, flags); - if (!ci) - return; - - ci->host = host; - ci->nodeid = nodeid; - ci->generation = generation; - - /* Prepare for speed probe which occurs when reading the ROM */ - speed = &(host->speed[NODEID_TO_NODE(nodeid)]); - if (*speed > host->csr.lnk_spd) - *speed = host->csr.lnk_spd; - ci->speed_unverified = *speed > IEEE1394_SPEED_100; - - /* We need to detect when the ConfigROM's generation has changed, - * so we only update the node's info when it needs to be. */ - - csr = csr1212_create_csr(&nodemgr_csr_ops, 5 * sizeof(quadlet_t), ci); - if (!csr || csr1212_parse_csr(csr) != CSR1212_SUCCESS) { - HPSB_ERR("Error parsing configrom for node " NODE_BUS_FMT, - NODE_BUS_ARGS(host, nodeid)); - if (csr) - csr1212_destroy_csr(csr); - kfree(ci); - return; - } - - if (csr->bus_info_data[1] != IEEE1394_BUSID_MAGIC) { - /* This isn't a 1394 device, but we let it slide. There - * was a report of a device with broken firmware which - * reported '2394' instead of '1394', which is obviously a - * mistake. One would hope that a non-1394 device never - * gets connected to Firewire bus. If someone does, we - * shouldn't be held responsible, so we'll allow it with a - * warning. */ - HPSB_WARN("Node " NODE_BUS_FMT " has invalid busID magic [0x%08x]", - NODE_BUS_ARGS(host, nodeid), csr->bus_info_data[1]); - } - - guid = ((u64)be32_to_cpu(csr->bus_info_data[3]) << 32) | be32_to_cpu(csr->bus_info_data[4]); - ne = find_entry_by_guid(guid); - - if (ne && ne->host != host && ne->in_limbo) { - /* Must have moved this device from one host to another */ - nodemgr_remove_ne(ne); - ne = NULL; - } - - if (!ne) - nodemgr_create_node(guid, csr, host, nodeid, generation); - else - nodemgr_update_node(ne, csr, nodeid, generation); -} - - -static void nodemgr_node_scan(struct hpsb_host *host, int generation) -{ - int count; - struct selfid *sid = (struct selfid *)host->topology_map; - nodeid_t nodeid = LOCAL_BUS; - - /* Scan each node on the bus */ - for (count = host->selfid_count; count; count--, sid++) { - if (sid->extended) - continue; - - if (!sid->link_active) { - nodeid++; - continue; - } - nodemgr_node_scan_one(host, nodeid++, generation); - } -} - -static void nodemgr_pause_ne(struct node_entry *ne) -{ - HPSB_DEBUG("Node paused: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", - NODE_BUS_ARGS(ne->host, ne->nodeid), - (unsigned long long)ne->guid); - - ne->in_limbo = true; - WARN_ON(device_create_file(&ne->device, &dev_attr_ne_in_limbo)); -} - -static int update_pdrv(struct device *dev, void *data) -{ - struct unit_directory *ud; - struct device_driver *drv; - struct hpsb_protocol_driver *pdrv; - struct node_entry *ne = data; - int error; - - ud = container_of(dev, struct unit_directory, unit_dev); - if (ud->ne == ne) { - drv = get_driver(ud->device.driver); - if (drv) { - error = 0; - pdrv = container_of(drv, struct hpsb_protocol_driver, - driver); - if (pdrv->update) { - device_lock(&ud->device); - error = pdrv->update(ud); - device_unlock(&ud->device); - } - if (error) - device_release_driver(&ud->device); - put_driver(drv); - } - } - - return 0; -} - -static void nodemgr_update_pdrv(struct node_entry *ne) -{ - class_for_each_device(&nodemgr_ud_class, NULL, ne, update_pdrv); -} - -/* Write the BROADCAST_CHANNEL as per IEEE1394a 8.3.2.3.11 and 8.4.2.3. This - * seems like an optional service but in the end it is practically mandatory - * as a consequence of these clauses. - * - * Note that we cannot do a broadcast write to all nodes at once because some - * pre-1394a devices would hang. */ -static void nodemgr_irm_write_bc(struct node_entry *ne, int generation) -{ - const u64 bc_addr = (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL); - quadlet_t bc_remote, bc_local; - int error; - - if (!ne->host->is_irm || ne->generation != generation || - ne->nodeid == ne->host->node_id) - return; - - bc_local = cpu_to_be32(ne->host->csr.broadcast_channel); - - /* Check if the register is implemented and 1394a compliant. */ - error = hpsb_read(ne->host, ne->nodeid, generation, bc_addr, &bc_remote, - sizeof(bc_remote)); - if (!error && bc_remote & cpu_to_be32(0x80000000) && - bc_remote != bc_local) - hpsb_node_write(ne, bc_addr, &bc_local, sizeof(bc_local)); -} - - -static void nodemgr_probe_ne(struct hpsb_host *host, struct node_entry *ne, - int generation) -{ - struct device *dev; - - if (ne->host != host || ne->in_limbo) - return; - - dev = get_device(&ne->device); - if (!dev) - return; - - nodemgr_irm_write_bc(ne, generation); - - /* If "needs_probe", then this is either a new or changed node we - * rescan totally. If the generation matches for an existing node - * (one that existed prior to the bus reset) we send update calls - * down to the drivers. Otherwise, this is a dead node and we - * suspend it. */ - if (ne->needs_probe) - nodemgr_process_root_directory(ne); - else if (ne->generation == generation) - nodemgr_update_pdrv(ne); - else - nodemgr_pause_ne(ne); - - put_device(dev); -} - -struct node_probe_parameter { - struct hpsb_host *host; - int generation; - bool probe_now; -}; - -static int node_probe(struct device *dev, void *data) -{ - struct node_probe_parameter *p = data; - struct node_entry *ne; - - if (p->generation != get_hpsb_generation(p->host)) - return -EAGAIN; - - ne = container_of(dev, struct node_entry, node_dev); - if (ne->needs_probe == p->probe_now) - nodemgr_probe_ne(p->host, ne, p->generation); - return 0; -} - -static int nodemgr_node_probe(struct hpsb_host *host, int generation) -{ - struct node_probe_parameter p; - - p.host = host; - p.generation = generation; - /* - * Do some processing of the nodes we've probed. This pulls them - * into the sysfs layer if needed, and can result in processing of - * unit-directories, or just updating the node and it's - * unit-directories. - * - * Run updates before probes. Usually, updates are time-critical - * while probes are time-consuming. - * - * Meanwhile, another bus reset may have happened. In this case we - * skip everything here and let the next bus scan handle it. - * Otherwise we may prematurely remove nodes which are still there. - */ - p.probe_now = false; - if (class_for_each_device(&nodemgr_ne_class, NULL, &p, node_probe) != 0) - return 0; - - p.probe_now = true; - if (class_for_each_device(&nodemgr_ne_class, NULL, &p, node_probe) != 0) - return 0; - /* - * Now let's tell the bus to rescan our devices. This may seem - * like overhead, but the driver-model core will only scan a - * device for a driver when either the device is added, or when a - * new driver is added. A bus reset is a good reason to rescan - * devices that were there before. For example, an sbp2 device - * may become available for login, if the host that held it was - * just removed. - */ - if (bus_rescan_devices(&ieee1394_bus_type) != 0) - HPSB_DEBUG("bus_rescan_devices had an error"); - - return 1; -} - -static int remove_nodes_in_limbo(struct device *dev, void *data) -{ - struct node_entry *ne; - - if (dev->bus != &ieee1394_bus_type) - return 0; - - ne = container_of(dev, struct node_entry, device); - if (ne->in_limbo) - nodemgr_remove_ne(ne); - - return 0; -} - -static void nodemgr_remove_nodes_in_limbo(struct hpsb_host *host) -{ - device_for_each_child(&host->device, NULL, remove_nodes_in_limbo); -} - -static int nodemgr_send_resume_packet(struct hpsb_host *host) -{ - struct hpsb_packet *packet; - int error = -ENOMEM; - - packet = hpsb_make_phypacket(host, - EXTPHYPACKET_TYPE_RESUME | - NODEID_TO_NODE(host->node_id) << PHYPACKET_PORT_SHIFT); - if (packet) { - packet->no_waiter = 1; - packet->generation = get_hpsb_generation(host); - error = hpsb_send_packet(packet); - } - if (error) - HPSB_WARN("fw-host%d: Failed to broadcast resume packet", - host->id); - return error; -} - -/* Perform a few high-level IRM responsibilities. */ -static int nodemgr_do_irm_duties(struct hpsb_host *host, int cycles) -{ - quadlet_t bc; - - /* if irm_id == -1 then there is no IRM on this bus */ - if (!host->is_irm || host->irm_id == (nodeid_t)-1) - return 1; - - /* We are a 1394a-2000 compliant IRM. Set the validity bit. */ - host->csr.broadcast_channel |= 0x40000000; - - /* If there is no bus manager then we should set the root node's - * force_root bit to promote bus stability per the 1394 - * spec. (8.4.2.6) */ - if (host->busmgr_id == 0xffff && host->node_count > 1) - { - u16 root_node = host->node_count - 1; - - /* get cycle master capability flag from root node */ - if (host->is_cycmst || - (!hpsb_read(host, LOCAL_BUS | root_node, get_hpsb_generation(host), - (CSR_REGISTER_BASE + CSR_CONFIG_ROM + 2 * sizeof(quadlet_t)), - &bc, sizeof(quadlet_t)) && - be32_to_cpu(bc) & 1 << CSR_CMC_SHIFT)) - hpsb_send_phy_config(host, root_node, -1); - else { - HPSB_DEBUG("The root node is not cycle master capable; " - "selecting a new root node and resetting..."); - - if (cycles >= 5) { - /* Oh screw it! Just leave the bus as it is */ - HPSB_DEBUG("Stopping reset loop for IRM sanity"); - return 1; - } - - hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1); - hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT); - - return 0; - } - } - - /* Some devices suspend their ports while being connected to an inactive - * host adapter, i.e. if connected before the low-level driver is - * loaded. They become visible either when physically unplugged and - * replugged, or when receiving a resume packet. Send one once. */ - if (!host->resume_packet_sent && !nodemgr_send_resume_packet(host)) - host->resume_packet_sent = 1; - - return 1; -} - -/* We need to ensure that if we are not the IRM, that the IRM node is capable of - * everything we can do, otherwise issue a bus reset and try to become the IRM - * ourselves. */ -static int nodemgr_check_irm_capability(struct hpsb_host *host, int cycles) -{ - quadlet_t bc; - int status; - - if (hpsb_disable_irm || host->is_irm) - return 1; - - status = hpsb_read(host, LOCAL_BUS | (host->irm_id), - get_hpsb_generation(host), - (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL), - &bc, sizeof(quadlet_t)); - - if (status < 0 || !(be32_to_cpu(bc) & 0x80000000)) { - /* The current irm node does not have a valid BROADCAST_CHANNEL - * register and we do, so reset the bus with force_root set */ - HPSB_DEBUG("Current remote IRM is not 1394a-2000 compliant, resetting..."); - - if (cycles >= 5) { - /* Oh screw it! Just leave the bus as it is */ - HPSB_DEBUG("Stopping reset loop for IRM sanity"); - return 1; - } - - hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1); - hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT); - - return 0; - } - - return 1; -} - -static int nodemgr_host_thread(void *data) -{ - struct hpsb_host *host = data; - unsigned int g, generation = 0; - int i, reset_cycles = 0; - - set_freezable(); - /* Setup our device-model entries */ - nodemgr_create_host_dev_files(host); - - for (;;) { - /* Sleep until next bus reset */ - set_current_state(TASK_INTERRUPTIBLE); - if (get_hpsb_generation(host) == generation && - !kthread_should_stop()) - schedule(); - __set_current_state(TASK_RUNNING); - - /* Thread may have been woken up to freeze or to exit */ - if (try_to_freeze()) - continue; - if (kthread_should_stop()) - goto exit; - - /* Pause for 1/4 second in 1/16 second intervals, - * to make sure things settle down. */ - g = get_hpsb_generation(host); - for (i = 0; i < 4 ; i++) { - msleep_interruptible(63); - try_to_freeze(); - if (kthread_should_stop()) - goto exit; - - /* Now get the generation in which the node ID's we collect - * are valid. During the bus scan we will use this generation - * for the read transactions, so that if another reset occurs - * during the scan the transactions will fail instead of - * returning bogus data. */ - generation = get_hpsb_generation(host); - - /* If we get a reset before we are done waiting, then - * start the waiting over again */ - if (generation != g) - g = generation, i = 0; - } - - if (!nodemgr_check_irm_capability(host, reset_cycles) || - !nodemgr_do_irm_duties(host, reset_cycles)) { - reset_cycles++; - continue; - } - reset_cycles = 0; - - /* Scan our nodes to get the bus options and create node - * entries. This does not do the sysfs stuff, since that - * would trigger uevents and such, which is a bad idea at - * this point. */ - nodemgr_node_scan(host, generation); - - /* This actually does the full probe, with sysfs - * registration. */ - if (!nodemgr_node_probe(host, generation)) - continue; - - /* Update some of our sysfs symlinks */ - nodemgr_update_host_dev_links(host); - - /* Sleep 3 seconds */ - for (i = 3000/200; i; i--) { - msleep_interruptible(200); - try_to_freeze(); - if (kthread_should_stop()) - goto exit; - - if (generation != get_hpsb_generation(host)) - break; - } - /* Remove nodes which are gone, unless a bus reset happened */ - if (!i) - nodemgr_remove_nodes_in_limbo(host); - } -exit: - HPSB_VERBOSE("NodeMgr: Exiting thread"); - return 0; -} - -struct per_host_parameter { - void *data; - int (*cb)(struct hpsb_host *, void *); -}; - -static int per_host(struct device *dev, void *data) -{ - struct hpsb_host *host; - struct per_host_parameter *p = data; - - host = container_of(dev, struct hpsb_host, host_dev); - return p->cb(host, p->data); -} - -/** - * nodemgr_for_each_host - call a function for each IEEE 1394 host - * @data: an address to supply to the callback - * @cb: function to call for each host - * - * Iterate the hosts, calling a given function with supplied data for each host. - * If the callback fails on a host, i.e. if it returns a non-zero value, the - * iteration is stopped. - * - * Return value: 0 on success, non-zero on failure (same as returned by last run - * of the callback). - */ -int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)) -{ - struct per_host_parameter p; - - p.cb = cb; - p.data = data; - return class_for_each_device(&hpsb_host_class, NULL, &p, per_host); -} - -/* The following two convenience functions use a struct node_entry - * for addressing a node on the bus. They are intended for use by any - * process context, not just the nodemgr thread, so we need to be a - * little careful when reading out the node ID and generation. The - * thing that can go wrong is that we get the node ID, then a bus - * reset occurs, and then we read the generation. The node ID is - * possibly invalid, but the generation is current, and we end up - * sending a packet to a the wrong node. - * - * The solution is to make sure we read the generation first, so that - * if a reset occurs in the process, we end up with a stale generation - * and the transactions will fail instead of silently using wrong node - * ID's. - */ - -/** - * hpsb_node_fill_packet - fill some destination information into a packet - * @ne: destination node - * @packet: packet to fill in - * - * This will fill in the given, pre-initialised hpsb_packet with the current - * information from the node entry (host, node ID, bus generation number). - */ -void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet) -{ - packet->host = ne->host; - packet->generation = ne->generation; - smp_rmb(); - packet->node_id = ne->nodeid; -} - -int hpsb_node_write(struct node_entry *ne, u64 addr, - quadlet_t *buffer, size_t length) -{ - unsigned int generation = ne->generation; - - smp_rmb(); - return hpsb_write(ne->host, ne->nodeid, generation, - addr, buffer, length); -} - -static void nodemgr_add_host(struct hpsb_host *host) -{ - struct host_info *hi; - - hi = hpsb_create_hostinfo(&nodemgr_highlevel, host, sizeof(*hi)); - if (!hi) { - HPSB_ERR("NodeMgr: out of memory in add host"); - return; - } - hi->host = host; - hi->thread = kthread_run(nodemgr_host_thread, host, "knodemgrd_%d", - host->id); - if (IS_ERR(hi->thread)) { - HPSB_ERR("NodeMgr: cannot start thread for host %d", host->id); - hpsb_destroy_hostinfo(&nodemgr_highlevel, host); - } -} - -static void nodemgr_host_reset(struct hpsb_host *host) -{ - struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host); - - if (hi) { - HPSB_VERBOSE("NodeMgr: Processing reset for host %d", host->id); - wake_up_process(hi->thread); - } -} - -static void nodemgr_remove_host(struct hpsb_host *host) -{ - struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host); - - if (hi) { - kthread_stop(hi->thread); - nodemgr_remove_host_dev(&host->device); - } -} - -static struct hpsb_highlevel nodemgr_highlevel = { - .name = "Node manager", - .add_host = nodemgr_add_host, - .host_reset = nodemgr_host_reset, - .remove_host = nodemgr_remove_host, -}; - -int init_ieee1394_nodemgr(void) -{ - int error; - - error = class_register(&nodemgr_ne_class); - if (error) - goto fail_ne; - error = class_register(&nodemgr_ud_class); - if (error) - goto fail_ud; - error = driver_register(&nodemgr_mid_layer_driver); - if (error) - goto fail_ml; - /* This driver is not used if nodemgr is off (disable_nodemgr=1). */ - nodemgr_dev_template_host.driver = &nodemgr_mid_layer_driver; - - hpsb_register_highlevel(&nodemgr_highlevel); - return 0; - -fail_ml: - class_unregister(&nodemgr_ud_class); -fail_ud: - class_unregister(&nodemgr_ne_class); -fail_ne: - return error; -} - -void cleanup_ieee1394_nodemgr(void) -{ - hpsb_unregister_highlevel(&nodemgr_highlevel); - driver_unregister(&nodemgr_mid_layer_driver); - class_unregister(&nodemgr_ud_class); - class_unregister(&nodemgr_ne_class); -} diff --git a/drivers/ieee1394/nodemgr.h b/drivers/ieee1394/nodemgr.h deleted file mode 100644 index 749b271d3107..000000000000 --- a/drivers/ieee1394/nodemgr.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2000 Andreas E. Bombe - * 2001 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _IEEE1394_NODEMGR_H -#define _IEEE1394_NODEMGR_H - -#include -#include -#include - -#include "ieee1394_core.h" -#include "ieee1394_transactions.h" -#include "ieee1394_types.h" - -struct csr1212_csr; -struct csr1212_keyval; -struct hpsb_host; -struct ieee1394_device_id; - -/* This is the start of a Node entry structure. It should be a stable API - * for which to gather info from the Node Manager about devices attached - * to the bus. */ -struct bus_options { - u8 irmc; /* Iso Resource Manager Capable */ - u8 cmc; /* Cycle Master Capable */ - u8 isc; /* Iso Capable */ - u8 bmc; /* Bus Master Capable */ - u8 pmc; /* Power Manager Capable (PNP spec) */ - u8 cyc_clk_acc; /* Cycle clock accuracy */ - u8 max_rom; /* Maximum block read supported in the CSR */ - u8 generation; /* Incremented when configrom changes */ - u8 lnkspd; /* Link speed */ - u16 max_rec; /* Maximum packet size node can receive */ -}; - -#define UNIT_DIRECTORY_VENDOR_ID 0x01 -#define UNIT_DIRECTORY_MODEL_ID 0x02 -#define UNIT_DIRECTORY_SPECIFIER_ID 0x04 -#define UNIT_DIRECTORY_VERSION 0x08 -#define UNIT_DIRECTORY_HAS_LUN_DIRECTORY 0x10 -#define UNIT_DIRECTORY_LUN_DIRECTORY 0x20 -#define UNIT_DIRECTORY_HAS_LUN 0x40 - -/* - * A unit directory corresponds to a protocol supported by the - * node. If a node supports eg. IP/1394 and AV/C, its config rom has a - * unit directory for each of these protocols. - */ -struct unit_directory { - struct node_entry *ne; /* The node which this directory belongs to */ - octlet_t address; /* Address of the unit directory on the node */ - u8 flags; /* Indicates which entries were read */ - - quadlet_t vendor_id; - struct csr1212_keyval *vendor_name_kv; - - quadlet_t model_id; - struct csr1212_keyval *model_name_kv; - quadlet_t specifier_id; - quadlet_t version; - quadlet_t directory_id; - - unsigned int id; - - int ignore_driver; - - int length; /* Number of quadlets */ - - struct device device; - struct device unit_dev; - - struct csr1212_keyval *ud_kv; - u32 lun; /* logical unit number immediate value */ -}; - -struct node_entry { - u64 guid; /* GUID of this node */ - u32 guid_vendor_id; /* Top 24bits of guid */ - - struct hpsb_host *host; /* Host this node is attached to */ - nodeid_t nodeid; /* NodeID */ - struct bus_options busopt; /* Bus Options */ - bool needs_probe; - unsigned int generation; /* Synced with hpsb generation */ - - /* The following is read from the config rom */ - u32 vendor_id; - struct csr1212_keyval *vendor_name_kv; - - u32 capabilities; - - struct device device; - struct device node_dev; - - /* Means this node is not attached anymore */ - bool in_limbo; - - struct csr1212_csr *csr; -}; - -struct hpsb_protocol_driver { - /* The name of the driver, e.g. SBP2 or IP1394 */ - const char *name; - - /* - * The device id table describing the protocols and/or devices - * supported by this driver. This is used by the nodemgr to - * decide if a driver could support a given node, but the - * probe function below can implement further protocol - * dependent or vendor dependent checking. - */ - const struct ieee1394_device_id *id_table; - - /* - * The update function is called when the node has just - * survived a bus reset, i.e. it is still present on the bus. - * However, it may be necessary to reestablish the connection - * or login into the node again, depending on the protocol. If the - * probe fails (returns non-zero), we unbind the driver from this - * device. - */ - int (*update)(struct unit_directory *ud); - - /* Our LDM structure */ - struct device_driver driver; -}; - -int __hpsb_register_protocol(struct hpsb_protocol_driver *, struct module *); -static inline int hpsb_register_protocol(struct hpsb_protocol_driver *driver) -{ - return __hpsb_register_protocol(driver, THIS_MODULE); -} - -void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver); - -static inline int hpsb_node_entry_valid(struct node_entry *ne) -{ - return ne->generation == get_hpsb_generation(ne->host); -} -void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet); -int hpsb_node_write(struct node_entry *ne, u64 addr, - quadlet_t *buffer, size_t length); -static inline int hpsb_node_read(struct node_entry *ne, u64 addr, - quadlet_t *buffer, size_t length) -{ - unsigned int g = ne->generation; - - smp_rmb(); - return hpsb_read(ne->host, ne->nodeid, g, addr, buffer, length); -} -static inline int hpsb_node_lock(struct node_entry *ne, u64 addr, int extcode, - quadlet_t *buffer, quadlet_t arg) -{ - unsigned int g = ne->generation; - - smp_rmb(); - return hpsb_lock(ne->host, ne->nodeid, g, addr, extcode, buffer, arg); -} -int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)); - -int init_ieee1394_nodemgr(void); -void cleanup_ieee1394_nodemgr(void); - -/* The template for a host device */ -extern struct device nodemgr_dev_template_host; - -/* Bus attributes we export */ -extern struct bus_attribute *const fw_bus_attrs[]; - -#endif /* _IEEE1394_NODEMGR_H */ diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c deleted file mode 100644 index 50815022cff1..000000000000 --- a/drivers/ieee1394/ohci1394.c +++ /dev/null @@ -1,3590 +0,0 @@ -/* - * ohci1394.c - driver for OHCI 1394 boards - * Copyright (C)1999,2000 Sebastien Rougeaux - * Gord Peters - * 2001 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - * Things known to be working: - * . Async Request Transmit - * . Async Response Receive - * . Async Request Receive - * . Async Response Transmit - * . Iso Receive - * . DMA mmap for iso receive - * . Config ROM generation - * - * Things implemented, but still in test phase: - * . Iso Transmit - * . Async Stream Packets Transmit (Receive done via Iso interface) - * - * Things not implemented: - * . DMA error recovery - * - * Known bugs: - * . devctl BUS_RESET arg confusion (reset type or root holdoff?) - * added LONG_RESET_ROOT and SHORT_RESET_ROOT for root holdoff --kk - */ - -/* - * Acknowledgments: - * - * Adam J Richter - * . Use of pci_class to find device - * - * Emilie Chung - * . Tip on Async Request Filter - * - * Pascal Drolet - * . Various tips for optimization and functionnalities - * - * Robert Ficklin - * . Loop in irq_handler - * - * James Goodwin - * . Various tips on initialization, self-id reception, etc. - * - * Albrecht Dress - * . Apple PowerBook detection - * - * Daniel Kobras - * . Reset the board properly before leaving + misc cleanups - * - * Leon van Stuivenberg - * . Bug fixes - * - * Ben Collins - * . Working big-endian support - * . Updated to 2.4.x module scheme (PCI aswell) - * . Config ROM generation - * - * Manfred Weihs - * . Reworked code for initiating bus resets - * (long, short, with or without hold-off) - * - * Nandu Santhi - * . Added support for nVidia nForce2 onboard Firewire chipset - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_PPC_PMAC -#include -#include -#include -#include -#endif - -#include "csr1212.h" -#include "ieee1394.h" -#include "ieee1394_types.h" -#include "hosts.h" -#include "dma.h" -#include "iso.h" -#include "ieee1394_core.h" -#include "highlevel.h" -#include "ohci1394.h" - -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG -#define OHCI1394_DEBUG -#endif - -#ifdef DBGMSG -#undef DBGMSG -#endif - -#ifdef OHCI1394_DEBUG -#define DBGMSG(fmt, args...) \ -printk(KERN_INFO "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args) -#else -#define DBGMSG(fmt, args...) do {} while (0) -#endif - -/* print general (card independent) information */ -#define PRINT_G(level, fmt, args...) \ -printk(level "%s: " fmt "\n" , OHCI1394_DRIVER_NAME , ## args) - -/* print card specific information */ -#define PRINT(level, fmt, args...) \ -printk(level "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args) - -/* Module Parameters */ -static int phys_dma = 1; -module_param(phys_dma, int, 0444); -MODULE_PARM_DESC(phys_dma, "Enable physical DMA (default = 1)."); - -static void dma_trm_tasklet(unsigned long data); -static void dma_trm_reset(struct dma_trm_ctx *d); - -static int alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d, - enum context_type type, int ctx, int num_desc, - int buf_size, int split_buf_size, int context_base); -static void free_dma_rcv_ctx(struct dma_rcv_ctx *d); - -static int alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d, - enum context_type type, int ctx, int num_desc, - int context_base); - -static void ohci1394_pci_remove(struct pci_dev *pdev); - -#ifndef __LITTLE_ENDIAN -static const size_t hdr_sizes[] = { - 3, /* TCODE_WRITEQ */ - 4, /* TCODE_WRITEB */ - 3, /* TCODE_WRITE_RESPONSE */ - 0, /* reserved */ - 3, /* TCODE_READQ */ - 4, /* TCODE_READB */ - 3, /* TCODE_READQ_RESPONSE */ - 4, /* TCODE_READB_RESPONSE */ - 1, /* TCODE_CYCLE_START */ - 4, /* TCODE_LOCK_REQUEST */ - 2, /* TCODE_ISO_DATA */ - 4, /* TCODE_LOCK_RESPONSE */ - /* rest is reserved or link-internal */ -}; - -static inline void header_le32_to_cpu(quadlet_t *data, unsigned char tcode) -{ - size_t size; - - if (unlikely(tcode >= ARRAY_SIZE(hdr_sizes))) - return; - - size = hdr_sizes[tcode]; - while (size--) - data[size] = le32_to_cpu(data[size]); -} -#else -#define header_le32_to_cpu(w,x) do {} while (0) -#endif /* !LITTLE_ENDIAN */ - -/*********************************** - * IEEE-1394 functionality section * - ***********************************/ - -static u8 get_phy_reg(struct ti_ohci *ohci, u8 addr) -{ - int i; - unsigned long flags; - quadlet_t r; - - spin_lock_irqsave (&ohci->phy_reg_lock, flags); - - reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | 0x00008000); - - for (i = 0; i < OHCI_LOOP_COUNT; i++) { - if (reg_read(ohci, OHCI1394_PhyControl) & 0x80000000) - break; - - mdelay(1); - } - - r = reg_read(ohci, OHCI1394_PhyControl); - - if (i >= OHCI_LOOP_COUNT) - PRINT (KERN_ERR, "Get PHY Reg timeout [0x%08x/0x%08x/%d]", - r, r & 0x80000000, i); - - spin_unlock_irqrestore (&ohci->phy_reg_lock, flags); - - return (r & 0x00ff0000) >> 16; -} - -static void set_phy_reg(struct ti_ohci *ohci, u8 addr, u8 data) -{ - int i; - unsigned long flags; - u32 r = 0; - - spin_lock_irqsave (&ohci->phy_reg_lock, flags); - - reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | data | 0x00004000); - - for (i = 0; i < OHCI_LOOP_COUNT; i++) { - r = reg_read(ohci, OHCI1394_PhyControl); - if (!(r & 0x00004000)) - break; - - mdelay(1); - } - - if (i == OHCI_LOOP_COUNT) - PRINT (KERN_ERR, "Set PHY Reg timeout [0x%08x/0x%08x/%d]", - r, r & 0x00004000, i); - - spin_unlock_irqrestore (&ohci->phy_reg_lock, flags); - - return; -} - -/* Or's our value into the current value */ -static void set_phy_reg_mask(struct ti_ohci *ohci, u8 addr, u8 data) -{ - u8 old; - - old = get_phy_reg (ohci, addr); - old |= data; - set_phy_reg (ohci, addr, old); - - return; -} - -static void handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host, - int phyid, int isroot) -{ - quadlet_t *q = ohci->selfid_buf_cpu; - quadlet_t self_id_count=reg_read(ohci, OHCI1394_SelfIDCount); - size_t size; - quadlet_t q0, q1; - - /* Check status of self-id reception */ - - if (ohci->selfid_swap) - q0 = le32_to_cpu(q[0]); - else - q0 = q[0]; - - if ((self_id_count & 0x80000000) || - ((self_id_count & 0x00FF0000) != (q0 & 0x00FF0000))) { - PRINT(KERN_ERR, - "Error in reception of SelfID packets [0x%08x/0x%08x] (count: %d)", - self_id_count, q0, ohci->self_id_errors); - - /* Tip by James Goodwin : - * We had an error, generate another bus reset in response. */ - if (ohci->self_id_errorsself_id_errors++; - } else { - PRINT(KERN_ERR, - "Too many errors on SelfID error reception, giving up!"); - } - return; - } - - /* SelfID Ok, reset error counter. */ - ohci->self_id_errors = 0; - - size = ((self_id_count & 0x00001FFC) >> 2) - 1; - q++; - - while (size > 0) { - if (ohci->selfid_swap) { - q0 = le32_to_cpu(q[0]); - q1 = le32_to_cpu(q[1]); - } else { - q0 = q[0]; - q1 = q[1]; - } - - if (q0 == ~q1) { - DBGMSG ("SelfID packet 0x%x received", q0); - hpsb_selfid_received(host, cpu_to_be32(q0)); - if (((q0 & 0x3f000000) >> 24) == phyid) - DBGMSG ("SelfID for this node is 0x%08x", q0); - } else { - PRINT(KERN_ERR, - "SelfID is inconsistent [0x%08x/0x%08x]", q0, q1); - } - q += 2; - size -= 2; - } - - DBGMSG("SelfID complete"); - - return; -} - -static void ohci_soft_reset(struct ti_ohci *ohci) { - int i; - - reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset); - - for (i = 0; i < OHCI_LOOP_COUNT; i++) { - if (!(reg_read(ohci, OHCI1394_HCControlSet) & OHCI1394_HCControl_softReset)) - break; - mdelay(1); - } - DBGMSG ("Soft reset finished"); -} - - -/* Generate the dma receive prgs and start the context */ -static void initialize_dma_rcv_ctx(struct dma_rcv_ctx *d, int generate_irq) -{ - struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); - int i; - - ohci1394_stop_context(ohci, d->ctrlClear, NULL); - - for (i=0; inum_desc; i++) { - u32 c; - - c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | DMA_CTL_BRANCH; - if (generate_irq) - c |= DMA_CTL_IRQ; - - d->prg_cpu[i]->control = cpu_to_le32(c | d->buf_size); - - /* End of descriptor list? */ - if (i + 1 < d->num_desc) { - d->prg_cpu[i]->branchAddress = - cpu_to_le32((d->prg_bus[i+1] & 0xfffffff0) | 0x1); - } else { - d->prg_cpu[i]->branchAddress = - cpu_to_le32((d->prg_bus[0] & 0xfffffff0)); - } - - d->prg_cpu[i]->address = cpu_to_le32(d->buf_bus[i]); - d->prg_cpu[i]->status = cpu_to_le32(d->buf_size); - } - - d->buf_ind = 0; - d->buf_offset = 0; - - if (d->type == DMA_CTX_ISO) { - /* Clear contextControl */ - reg_write(ohci, d->ctrlClear, 0xffffffff); - - /* Set bufferFill, isochHeader, multichannel for IR context */ - reg_write(ohci, d->ctrlSet, 0xd0000000); - - /* Set the context match register to match on all tags */ - reg_write(ohci, d->ctxtMatch, 0xf0000000); - - /* Clear the multi channel mask high and low registers */ - reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff); - reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff); - - /* Set up isoRecvIntMask to generate interrupts */ - reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << d->ctx); - } - - /* Tell the controller where the first AR program is */ - reg_write(ohci, d->cmdPtr, d->prg_bus[0] | 0x1); - - /* Run context */ - reg_write(ohci, d->ctrlSet, 0x00008000); - - DBGMSG("Receive DMA ctx=%d initialized", d->ctx); -} - -/* Initialize the dma transmit context */ -static void initialize_dma_trm_ctx(struct dma_trm_ctx *d) -{ - struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); - - /* Stop the context */ - ohci1394_stop_context(ohci, d->ctrlClear, NULL); - - d->prg_ind = 0; - d->sent_ind = 0; - d->free_prgs = d->num_desc; - d->branchAddrPtr = NULL; - INIT_LIST_HEAD(&d->fifo_list); - INIT_LIST_HEAD(&d->pending_list); - - if (d->type == DMA_CTX_ISO) { - /* enable interrupts */ - reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << d->ctx); - } - - DBGMSG("Transmit DMA ctx=%d initialized", d->ctx); -} - -/* Count the number of available iso contexts */ -static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg) -{ - u32 tmp; - - reg_write(ohci, reg, 0xffffffff); - tmp = reg_read(ohci, reg); - - DBGMSG("Iso contexts reg: %08x implemented: %08x", reg, tmp); - - /* Count the number of contexts */ - return hweight32(tmp); -} - -/* Global initialization */ -static void ohci_initialize(struct ti_ohci *ohci) -{ - quadlet_t buf; - int num_ports, i; - - spin_lock_init(&ohci->phy_reg_lock); - - /* Put some defaults to these undefined bus options */ - buf = reg_read(ohci, OHCI1394_BusOptions); - buf |= 0x60000000; /* Enable CMC and ISC */ - if (hpsb_disable_irm) - buf &= ~0x80000000; - else - buf |= 0x80000000; /* Enable IRMC */ - buf &= ~0x00ff0000; /* XXX: Set cyc_clk_acc to zero for now */ - buf &= ~0x18000000; /* Disable PMC and BMC */ - reg_write(ohci, OHCI1394_BusOptions, buf); - - /* Set the bus number */ - reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0); - - /* Enable posted writes */ - reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_postedWriteEnable); - - /* Clear link control register */ - reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff); - - /* Enable cycle timer and cycle master and set the IRM - * contender bit in our self ID packets if appropriate. */ - reg_write(ohci, OHCI1394_LinkControlSet, - OHCI1394_LinkControl_CycleTimerEnable | - OHCI1394_LinkControl_CycleMaster); - i = get_phy_reg(ohci, 4) | PHY_04_LCTRL; - if (hpsb_disable_irm) - i &= ~PHY_04_CONTENDER; - else - i |= PHY_04_CONTENDER; - set_phy_reg(ohci, 4, i); - - /* Set up self-id dma buffer */ - reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus); - - /* enable self-id */ - reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_RcvSelfID); - - /* Set the Config ROM mapping register */ - reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus); - - /* Now get our max packet size */ - ohci->max_packet_size = - 1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1); - - /* Clear the interrupt mask */ - reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); - - /* Clear the interrupt mask */ - reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff); - - /* Initialize AR dma */ - initialize_dma_rcv_ctx(&ohci->ar_req_context, 0); - initialize_dma_rcv_ctx(&ohci->ar_resp_context, 0); - - /* Initialize AT dma */ - initialize_dma_trm_ctx(&ohci->at_req_context); - initialize_dma_trm_ctx(&ohci->at_resp_context); - - /* Accept AR requests from all nodes */ - reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000); - - /* Set the address range of the physical response unit. - * Most controllers do not implement it as a writable register though. - * They will keep a hardwired offset of 0x00010000 and show 0x0 as - * register content. - * To actually enable physical responses is the job of our interrupt - * handler which programs the physical request filter. */ - reg_write(ohci, OHCI1394_PhyUpperBound, - OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED >> 16); - - DBGMSG("physUpperBoundOffset=%08x", - reg_read(ohci, OHCI1394_PhyUpperBound)); - - /* Specify AT retries */ - reg_write(ohci, OHCI1394_ATRetries, - OHCI1394_MAX_AT_REQ_RETRIES | - (OHCI1394_MAX_AT_RESP_RETRIES<<4) | - (OHCI1394_MAX_PHYS_RESP_RETRIES<<8)); - - /* We don't want hardware swapping */ - reg_write(ohci, OHCI1394_HCControlClear, OHCI1394_HCControl_noByteSwap); - - /* Enable interrupts */ - reg_write(ohci, OHCI1394_IntMaskSet, - OHCI1394_unrecoverableError | - OHCI1394_masterIntEnable | - OHCI1394_busReset | - OHCI1394_selfIDComplete | - OHCI1394_RSPkt | - OHCI1394_RQPkt | - OHCI1394_respTxComplete | - OHCI1394_reqTxComplete | - OHCI1394_isochRx | - OHCI1394_isochTx | - OHCI1394_postedWriteErr | - OHCI1394_cycleTooLong | - OHCI1394_cycleInconsistent); - - /* Enable link */ - reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable); - - buf = reg_read(ohci, OHCI1394_Version); - PRINT(KERN_INFO, "OHCI-1394 %d.%d (PCI): IRQ=[%d] " - "MMIO=[%llx-%llx] Max Packet=[%d] IR/IT contexts=[%d/%d]", - ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10), - ((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), ohci->dev->irq, - (unsigned long long)pci_resource_start(ohci->dev, 0), - (unsigned long long)pci_resource_start(ohci->dev, 0) + OHCI1394_REGISTER_SIZE - 1, - ohci->max_packet_size, - ohci->nb_iso_rcv_ctx, ohci->nb_iso_xmit_ctx); - - /* Check all of our ports to make sure that if anything is - * connected, we enable that port. */ - num_ports = get_phy_reg(ohci, 2) & 0xf; - for (i = 0; i < num_ports; i++) { - unsigned int status; - - set_phy_reg(ohci, 7, i); - status = get_phy_reg(ohci, 8); - - if (status & 0x20) - set_phy_reg(ohci, 8, status & ~1); - } - - /* Serial EEPROM Sanity check. */ - if ((ohci->max_packet_size < 512) || - (ohci->max_packet_size > 4096)) { - /* Serial EEPROM contents are suspect, set a sane max packet - * size and print the raw contents for bug reports if verbose - * debug is enabled. */ -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG - int i; -#endif - - PRINT(KERN_DEBUG, "Serial EEPROM has suspicious values, " - "attempting to set max_packet_size to 512 bytes"); - reg_write(ohci, OHCI1394_BusOptions, - (reg_read(ohci, OHCI1394_BusOptions) & 0xf007) | 0x8002); - ohci->max_packet_size = 512; -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG - PRINT(KERN_DEBUG, " EEPROM Present: %d", - (reg_read(ohci, OHCI1394_Version) >> 24) & 0x1); - reg_write(ohci, OHCI1394_GUID_ROM, 0x80000000); - - for (i = 0; - ((i < 1000) && - (reg_read(ohci, OHCI1394_GUID_ROM) & 0x80000000)); i++) - udelay(10); - - for (i = 0; i < 0x20; i++) { - reg_write(ohci, OHCI1394_GUID_ROM, 0x02000000); - PRINT(KERN_DEBUG, " EEPROM %02x: %02x", i, - (reg_read(ohci, OHCI1394_GUID_ROM) >> 16) & 0xff); - } -#endif - } -} - -/* - * Insert a packet in the DMA fifo and generate the DMA prg - * FIXME: rewrite the program in order to accept packets crossing - * page boundaries. - * check also that a single dma descriptor doesn't cross a - * page boundary. - */ -static void insert_packet(struct ti_ohci *ohci, - struct dma_trm_ctx *d, struct hpsb_packet *packet) -{ - u32 cycleTimer; - int idx = d->prg_ind; - - DBGMSG("Inserting packet for node " NODE_BUS_FMT - ", tlabel=%d, tcode=0x%x, speed=%d", - NODE_BUS_ARGS(ohci->host, packet->node_id), packet->tlabel, - packet->tcode, packet->speed_code); - - d->prg_cpu[idx]->begin.address = 0; - d->prg_cpu[idx]->begin.branchAddress = 0; - - if (d->type == DMA_CTX_ASYNC_RESP) { - /* - * For response packets, we need to put a timeout value in - * the 16 lower bits of the status... let's try 1 sec timeout - */ - cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - d->prg_cpu[idx]->begin.status = cpu_to_le32( - (((((cycleTimer>>25)&0x7)+1)&0x7)<<13) | - ((cycleTimer&0x01fff000)>>12)); - - DBGMSG("cycleTimer: %08x timeStamp: %08x", - cycleTimer, d->prg_cpu[idx]->begin.status); - } else - d->prg_cpu[idx]->begin.status = 0; - - if ( (packet->type == hpsb_async) || (packet->type == hpsb_raw) ) { - - if (packet->type == hpsb_raw) { - d->prg_cpu[idx]->data[0] = cpu_to_le32(OHCI1394_TCODE_PHY<<4); - d->prg_cpu[idx]->data[1] = cpu_to_le32(packet->header[0]); - d->prg_cpu[idx]->data[2] = cpu_to_le32(packet->header[1]); - } else { - d->prg_cpu[idx]->data[0] = packet->speed_code<<16 | - (packet->header[0] & 0xFFFF); - - if (packet->tcode == TCODE_ISO_DATA) { - /* Sending an async stream packet */ - d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000; - } else { - /* Sending a normal async request or response */ - d->prg_cpu[idx]->data[1] = - (packet->header[1] & 0xFFFF) | - (packet->header[0] & 0xFFFF0000); - d->prg_cpu[idx]->data[2] = packet->header[2]; - d->prg_cpu[idx]->data[3] = packet->header[3]; - } - header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode); - } - - if (packet->data_size) { /* block transmit */ - if (packet->tcode == TCODE_STREAM_DATA){ - d->prg_cpu[idx]->begin.control = - cpu_to_le32(DMA_CTL_OUTPUT_MORE | - DMA_CTL_IMMEDIATE | 0x8); - } else { - d->prg_cpu[idx]->begin.control = - cpu_to_le32(DMA_CTL_OUTPUT_MORE | - DMA_CTL_IMMEDIATE | 0x10); - } - d->prg_cpu[idx]->end.control = - cpu_to_le32(DMA_CTL_OUTPUT_LAST | - DMA_CTL_IRQ | - DMA_CTL_BRANCH | - packet->data_size); - /* - * Check that the packet data buffer - * does not cross a page boundary. - * - * XXX Fix this some day. eth1394 seems to trigger - * it, but ignoring it doesn't seem to cause a - * problem. - */ -#if 0 - if (cross_bound((unsigned long)packet->data, - packet->data_size)>0) { - /* FIXME: do something about it */ - PRINT(KERN_ERR, - "%s: packet data addr: %p size %Zd bytes " - "cross page boundary", __func__, - packet->data, packet->data_size); - } -#endif - d->prg_cpu[idx]->end.address = cpu_to_le32( - pci_map_single(ohci->dev, packet->data, - packet->data_size, - PCI_DMA_TODEVICE)); - - d->prg_cpu[idx]->end.branchAddress = 0; - d->prg_cpu[idx]->end.status = 0; - if (d->branchAddrPtr) - *(d->branchAddrPtr) = - cpu_to_le32(d->prg_bus[idx] | 0x3); - d->branchAddrPtr = - &(d->prg_cpu[idx]->end.branchAddress); - } else { /* quadlet transmit */ - if (packet->type == hpsb_raw) - d->prg_cpu[idx]->begin.control = - cpu_to_le32(DMA_CTL_OUTPUT_LAST | - DMA_CTL_IMMEDIATE | - DMA_CTL_IRQ | - DMA_CTL_BRANCH | - (packet->header_size + 4)); - else - d->prg_cpu[idx]->begin.control = - cpu_to_le32(DMA_CTL_OUTPUT_LAST | - DMA_CTL_IMMEDIATE | - DMA_CTL_IRQ | - DMA_CTL_BRANCH | - packet->header_size); - - if (d->branchAddrPtr) - *(d->branchAddrPtr) = - cpu_to_le32(d->prg_bus[idx] | 0x2); - d->branchAddrPtr = - &(d->prg_cpu[idx]->begin.branchAddress); - } - - } else { /* iso packet */ - d->prg_cpu[idx]->data[0] = packet->speed_code<<16 | - (packet->header[0] & 0xFFFF); - d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000; - header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode); - - d->prg_cpu[idx]->begin.control = - cpu_to_le32(DMA_CTL_OUTPUT_MORE | - DMA_CTL_IMMEDIATE | 0x8); - d->prg_cpu[idx]->end.control = - cpu_to_le32(DMA_CTL_OUTPUT_LAST | - DMA_CTL_UPDATE | - DMA_CTL_IRQ | - DMA_CTL_BRANCH | - packet->data_size); - d->prg_cpu[idx]->end.address = cpu_to_le32( - pci_map_single(ohci->dev, packet->data, - packet->data_size, PCI_DMA_TODEVICE)); - - d->prg_cpu[idx]->end.branchAddress = 0; - d->prg_cpu[idx]->end.status = 0; - DBGMSG("Iso xmit context info: header[%08x %08x]\n" - " begin=%08x %08x %08x %08x\n" - " %08x %08x %08x %08x\n" - " end =%08x %08x %08x %08x", - d->prg_cpu[idx]->data[0], d->prg_cpu[idx]->data[1], - d->prg_cpu[idx]->begin.control, - d->prg_cpu[idx]->begin.address, - d->prg_cpu[idx]->begin.branchAddress, - d->prg_cpu[idx]->begin.status, - d->prg_cpu[idx]->data[0], - d->prg_cpu[idx]->data[1], - d->prg_cpu[idx]->data[2], - d->prg_cpu[idx]->data[3], - d->prg_cpu[idx]->end.control, - d->prg_cpu[idx]->end.address, - d->prg_cpu[idx]->end.branchAddress, - d->prg_cpu[idx]->end.status); - if (d->branchAddrPtr) - *(d->branchAddrPtr) = cpu_to_le32(d->prg_bus[idx] | 0x3); - d->branchAddrPtr = &(d->prg_cpu[idx]->end.branchAddress); - } - d->free_prgs--; - - /* queue the packet in the appropriate context queue */ - list_add_tail(&packet->driver_list, &d->fifo_list); - d->prg_ind = (d->prg_ind + 1) % d->num_desc; -} - -/* - * This function fills the FIFO with the (eventual) pending packets - * and runs or wakes up the DMA prg if necessary. - * - * The function MUST be called with the d->lock held. - */ -static void dma_trm_flush(struct ti_ohci *ohci, struct dma_trm_ctx *d) -{ - struct hpsb_packet *packet, *ptmp; - int idx = d->prg_ind; - int z = 0; - - /* insert the packets into the dma fifo */ - list_for_each_entry_safe(packet, ptmp, &d->pending_list, driver_list) { - if (!d->free_prgs) - break; - - /* For the first packet only */ - if (!z) - z = (packet->data_size) ? 3 : 2; - - /* Insert the packet */ - list_del_init(&packet->driver_list); - insert_packet(ohci, d, packet); - } - - /* Nothing must have been done, either no free_prgs or no packets */ - if (z == 0) - return; - - /* Is the context running ? (should be unless it is - the first packet to be sent in this context) */ - if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) { - u32 nodeId = reg_read(ohci, OHCI1394_NodeID); - - DBGMSG("Starting transmit DMA ctx=%d",d->ctx); - reg_write(ohci, d->cmdPtr, d->prg_bus[idx] | z); - - /* Check that the node id is valid, and not 63 */ - if (!(nodeId & 0x80000000) || (nodeId & 0x3f) == 63) - PRINT(KERN_ERR, "Running dma failed because Node ID is not valid"); - else - reg_write(ohci, d->ctrlSet, 0x8000); - } else { - /* Wake up the dma context if necessary */ - if (!(reg_read(ohci, d->ctrlSet) & 0x400)) - DBGMSG("Waking transmit DMA ctx=%d",d->ctx); - - /* do this always, to avoid race condition */ - reg_write(ohci, d->ctrlSet, 0x1000); - } - - return; -} - -/* Transmission of an async or iso packet */ -static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet) -{ - struct ti_ohci *ohci = host->hostdata; - struct dma_trm_ctx *d; - unsigned long flags; - - if (packet->data_size > ohci->max_packet_size) { - PRINT(KERN_ERR, - "Transmit packet size %Zd is too big", - packet->data_size); - return -EOVERFLOW; - } - - if (packet->type == hpsb_raw) - d = &ohci->at_req_context; - else if ((packet->tcode & 0x02) && (packet->tcode != TCODE_ISO_DATA)) - d = &ohci->at_resp_context; - else - d = &ohci->at_req_context; - - spin_lock_irqsave(&d->lock,flags); - - list_add_tail(&packet->driver_list, &d->pending_list); - - dma_trm_flush(ohci, d); - - spin_unlock_irqrestore(&d->lock,flags); - - return 0; -} - -static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) -{ - struct ti_ohci *ohci = host->hostdata; - int retval = 0, phy_reg; - - switch (cmd) { - case RESET_BUS: - switch (arg) { - case SHORT_RESET: - phy_reg = get_phy_reg(ohci, 5); - phy_reg |= 0x40; - set_phy_reg(ohci, 5, phy_reg); /* set ISBR */ - break; - case LONG_RESET: - phy_reg = get_phy_reg(ohci, 1); - phy_reg |= 0x40; - set_phy_reg(ohci, 1, phy_reg); /* set IBR */ - break; - case SHORT_RESET_NO_FORCE_ROOT: - phy_reg = get_phy_reg(ohci, 1); - if (phy_reg & 0x80) { - phy_reg &= ~0x80; - set_phy_reg(ohci, 1, phy_reg); /* clear RHB */ - } - - phy_reg = get_phy_reg(ohci, 5); - phy_reg |= 0x40; - set_phy_reg(ohci, 5, phy_reg); /* set ISBR */ - break; - case LONG_RESET_NO_FORCE_ROOT: - phy_reg = get_phy_reg(ohci, 1); - phy_reg &= ~0x80; - phy_reg |= 0x40; - set_phy_reg(ohci, 1, phy_reg); /* clear RHB, set IBR */ - break; - case SHORT_RESET_FORCE_ROOT: - phy_reg = get_phy_reg(ohci, 1); - if (!(phy_reg & 0x80)) { - phy_reg |= 0x80; - set_phy_reg(ohci, 1, phy_reg); /* set RHB */ - } - - phy_reg = get_phy_reg(ohci, 5); - phy_reg |= 0x40; - set_phy_reg(ohci, 5, phy_reg); /* set ISBR */ - break; - case LONG_RESET_FORCE_ROOT: - phy_reg = get_phy_reg(ohci, 1); - phy_reg |= 0xc0; - set_phy_reg(ohci, 1, phy_reg); /* set RHB and IBR */ - break; - default: - retval = -1; - } - break; - - case GET_CYCLE_COUNTER: - retval = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - break; - - case SET_CYCLE_COUNTER: - reg_write(ohci, OHCI1394_IsochronousCycleTimer, arg); - break; - - case SET_BUS_ID: - PRINT(KERN_ERR, "devctl command SET_BUS_ID err"); - break; - - case ACT_CYCLE_MASTER: - if (arg) { - /* check if we are root and other nodes are present */ - u32 nodeId = reg_read(ohci, OHCI1394_NodeID); - if ((nodeId & (1<<30)) && (nodeId & 0x3f)) { - /* - * enable cycleTimer, cycleMaster - */ - DBGMSG("Cycle master enabled"); - reg_write(ohci, OHCI1394_LinkControlSet, - OHCI1394_LinkControl_CycleTimerEnable | - OHCI1394_LinkControl_CycleMaster); - } - } else { - /* disable cycleTimer, cycleMaster, cycleSource */ - reg_write(ohci, OHCI1394_LinkControlClear, - OHCI1394_LinkControl_CycleTimerEnable | - OHCI1394_LinkControl_CycleMaster | - OHCI1394_LinkControl_CycleSource); - } - break; - - case CANCEL_REQUESTS: - DBGMSG("Cancel request received"); - dma_trm_reset(&ohci->at_req_context); - dma_trm_reset(&ohci->at_resp_context); - break; - - default: - PRINT_G(KERN_ERR, "ohci_devctl cmd %d not implemented yet", - cmd); - break; - } - return retval; -} - -/*********************************** - * rawiso ISO reception * - ***********************************/ - -/* - We use either buffer-fill or packet-per-buffer DMA mode. The DMA - buffer is split into "blocks" (regions described by one DMA - descriptor). Each block must be one page or less in size, and - must not cross a page boundary. - - There is one little wrinkle with buffer-fill mode: a packet that - starts in the final block may wrap around into the first block. But - the user API expects all packets to be contiguous. Our solution is - to keep the very last page of the DMA buffer in reserve - if a - packet spans the gap, we copy its tail into this page. -*/ - -struct ohci_iso_recv { - struct ti_ohci *ohci; - - struct ohci1394_iso_tasklet task; - int task_active; - - enum { BUFFER_FILL_MODE = 0, - PACKET_PER_BUFFER_MODE = 1 } dma_mode; - - /* memory and PCI mapping for the DMA descriptors */ - struct dma_prog_region prog; - struct dma_cmd *block; /* = (struct dma_cmd*) prog.virt */ - - /* how many DMA blocks fit in the buffer */ - unsigned int nblocks; - - /* stride of DMA blocks */ - unsigned int buf_stride; - - /* number of blocks to batch between interrupts */ - int block_irq_interval; - - /* block that DMA will finish next */ - int block_dma; - - /* (buffer-fill only) block that the reader will release next */ - int block_reader; - - /* (buffer-fill only) bytes of buffer the reader has released, - less than one block */ - int released_bytes; - - /* (buffer-fill only) buffer offset at which the next packet will appear */ - int dma_offset; - - /* OHCI DMA context control registers */ - u32 ContextControlSet; - u32 ContextControlClear; - u32 CommandPtr; - u32 ContextMatch; -}; - -static void ohci_iso_recv_task(unsigned long data); -static void ohci_iso_recv_stop(struct hpsb_iso *iso); -static void ohci_iso_recv_shutdown(struct hpsb_iso *iso); -static int ohci_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync); -static void ohci_iso_recv_program(struct hpsb_iso *iso); - -static int ohci_iso_recv_init(struct hpsb_iso *iso) -{ - struct ti_ohci *ohci = iso->host->hostdata; - struct ohci_iso_recv *recv; - int ctx; - int ret = -ENOMEM; - - recv = kmalloc(sizeof(*recv), GFP_KERNEL); - if (!recv) - return -ENOMEM; - - iso->hostdata = recv; - recv->ohci = ohci; - recv->task_active = 0; - dma_prog_region_init(&recv->prog); - recv->block = NULL; - - /* use buffer-fill mode, unless irq_interval is 1 - (note: multichannel requires buffer-fill) */ - - if (((iso->irq_interval == 1 && iso->dma_mode == HPSB_ISO_DMA_OLD_ABI) || - iso->dma_mode == HPSB_ISO_DMA_PACKET_PER_BUFFER) && iso->channel != -1) { - recv->dma_mode = PACKET_PER_BUFFER_MODE; - } else { - recv->dma_mode = BUFFER_FILL_MODE; - } - - /* set nblocks, buf_stride, block_irq_interval */ - - if (recv->dma_mode == BUFFER_FILL_MODE) { - recv->buf_stride = PAGE_SIZE; - - /* one block per page of data in the DMA buffer, minus the final guard page */ - recv->nblocks = iso->buf_size/PAGE_SIZE - 1; - if (recv->nblocks < 3) { - DBGMSG("ohci_iso_recv_init: DMA buffer too small"); - goto err; - } - - /* iso->irq_interval is in packets - translate that to blocks */ - if (iso->irq_interval == 1) - recv->block_irq_interval = 1; - else - recv->block_irq_interval = iso->irq_interval * - ((recv->nblocks+1)/iso->buf_packets); - if (recv->block_irq_interval*4 > recv->nblocks) - recv->block_irq_interval = recv->nblocks/4; - if (recv->block_irq_interval < 1) - recv->block_irq_interval = 1; - - } else { - int max_packet_size; - - recv->nblocks = iso->buf_packets; - recv->block_irq_interval = iso->irq_interval; - if (recv->block_irq_interval * 4 > iso->buf_packets) - recv->block_irq_interval = iso->buf_packets / 4; - if (recv->block_irq_interval < 1) - recv->block_irq_interval = 1; - - /* choose a buffer stride */ - /* must be a power of 2, and <= PAGE_SIZE */ - - max_packet_size = iso->buf_size / iso->buf_packets; - - for (recv->buf_stride = 8; recv->buf_stride < max_packet_size; - recv->buf_stride *= 2); - - if (recv->buf_stride*iso->buf_packets > iso->buf_size || - recv->buf_stride > PAGE_SIZE) { - /* this shouldn't happen, but anyway... */ - DBGMSG("ohci_iso_recv_init: problem choosing a buffer stride"); - goto err; - } - } - - recv->block_reader = 0; - recv->released_bytes = 0; - recv->block_dma = 0; - recv->dma_offset = 0; - - /* size of DMA program = one descriptor per block */ - if (dma_prog_region_alloc(&recv->prog, - sizeof(struct dma_cmd) * recv->nblocks, - recv->ohci->dev)) - goto err; - - recv->block = (struct dma_cmd*) recv->prog.kvirt; - - ohci1394_init_iso_tasklet(&recv->task, - iso->channel == -1 ? OHCI_ISO_MULTICHANNEL_RECEIVE : - OHCI_ISO_RECEIVE, - ohci_iso_recv_task, (unsigned long) iso); - - if (ohci1394_register_iso_tasklet(recv->ohci, &recv->task) < 0) { - ret = -EBUSY; - goto err; - } - - recv->task_active = 1; - - /* recv context registers are spaced 32 bytes apart */ - ctx = recv->task.context; - recv->ContextControlSet = OHCI1394_IsoRcvContextControlSet + 32 * ctx; - recv->ContextControlClear = OHCI1394_IsoRcvContextControlClear + 32 * ctx; - recv->CommandPtr = OHCI1394_IsoRcvCommandPtr + 32 * ctx; - recv->ContextMatch = OHCI1394_IsoRcvContextMatch + 32 * ctx; - - if (iso->channel == -1) { - /* clear multi-channel selection mask */ - reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, 0xFFFFFFFF); - reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, 0xFFFFFFFF); - } - - /* write the DMA program */ - ohci_iso_recv_program(iso); - - DBGMSG("ohci_iso_recv_init: %s mode, DMA buffer is %lu pages" - " (%u bytes), using %u blocks, buf_stride %u, block_irq_interval %d", - recv->dma_mode == BUFFER_FILL_MODE ? - "buffer-fill" : "packet-per-buffer", - iso->buf_size/PAGE_SIZE, iso->buf_size, - recv->nblocks, recv->buf_stride, recv->block_irq_interval); - - return 0; - -err: - ohci_iso_recv_shutdown(iso); - return ret; -} - -static void ohci_iso_recv_stop(struct hpsb_iso *iso) -{ - struct ohci_iso_recv *recv = iso->hostdata; - - /* disable interrupts */ - reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskClear, 1 << recv->task.context); - - /* halt DMA */ - ohci1394_stop_context(recv->ohci, recv->ContextControlClear, NULL); -} - -static void ohci_iso_recv_shutdown(struct hpsb_iso *iso) -{ - struct ohci_iso_recv *recv = iso->hostdata; - - if (recv->task_active) { - ohci_iso_recv_stop(iso); - ohci1394_unregister_iso_tasklet(recv->ohci, &recv->task); - recv->task_active = 0; - } - - dma_prog_region_free(&recv->prog); - kfree(recv); - iso->hostdata = NULL; -} - -/* set up a "gapped" ring buffer DMA program */ -static void ohci_iso_recv_program(struct hpsb_iso *iso) -{ - struct ohci_iso_recv *recv = iso->hostdata; - int blk; - - /* address of 'branch' field in previous DMA descriptor */ - u32 *prev_branch = NULL; - - for (blk = 0; blk < recv->nblocks; blk++) { - u32 control; - - /* the DMA descriptor */ - struct dma_cmd *cmd = &recv->block[blk]; - - /* offset of the DMA descriptor relative to the DMA prog buffer */ - unsigned long prog_offset = blk * sizeof(struct dma_cmd); - - /* offset of this packet's data within the DMA buffer */ - unsigned long buf_offset = blk * recv->buf_stride; - - if (recv->dma_mode == BUFFER_FILL_MODE) { - control = 2 << 28; /* INPUT_MORE */ - } else { - control = 3 << 28; /* INPUT_LAST */ - } - - control |= 8 << 24; /* s = 1, update xferStatus and resCount */ - - /* interrupt on last block, and at intervals */ - if (blk == recv->nblocks-1 || (blk % recv->block_irq_interval) == 0) { - control |= 3 << 20; /* want interrupt */ - } - - control |= 3 << 18; /* enable branch to address */ - control |= recv->buf_stride; - - cmd->control = cpu_to_le32(control); - cmd->address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, buf_offset)); - cmd->branchAddress = 0; /* filled in on next loop */ - cmd->status = cpu_to_le32(recv->buf_stride); - - /* link the previous descriptor to this one */ - if (prev_branch) { - *prev_branch = cpu_to_le32(dma_prog_region_offset_to_bus(&recv->prog, prog_offset) | 1); - } - - prev_branch = &cmd->branchAddress; - } - - /* the final descriptor's branch address and Z should be left at 0 */ -} - -/* listen or unlisten to a specific channel (multi-channel mode only) */ -static void ohci_iso_recv_change_channel(struct hpsb_iso *iso, unsigned char channel, int listen) -{ - struct ohci_iso_recv *recv = iso->hostdata; - int reg, i; - - if (channel < 32) { - reg = listen ? OHCI1394_IRMultiChanMaskLoSet : OHCI1394_IRMultiChanMaskLoClear; - i = channel; - } else { - reg = listen ? OHCI1394_IRMultiChanMaskHiSet : OHCI1394_IRMultiChanMaskHiClear; - i = channel - 32; - } - - reg_write(recv->ohci, reg, (1 << i)); - - /* issue a dummy read to force all PCI writes to be posted immediately */ - mb(); - reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer); -} - -static void ohci_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask) -{ - struct ohci_iso_recv *recv = iso->hostdata; - int i; - - for (i = 0; i < 64; i++) { - if (mask & (1ULL << i)) { - if (i < 32) - reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoSet, (1 << i)); - else - reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiSet, (1 << (i-32))); - } else { - if (i < 32) - reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, (1 << i)); - else - reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, (1 << (i-32))); - } - } - - /* issue a dummy read to force all PCI writes to be posted immediately */ - mb(); - reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer); -} - -static int ohci_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync) -{ - struct ohci_iso_recv *recv = iso->hostdata; - struct ti_ohci *ohci = recv->ohci; - u32 command, contextMatch; - - reg_write(recv->ohci, recv->ContextControlClear, 0xFFFFFFFF); - wmb(); - - /* always keep ISO headers */ - command = (1 << 30); - - if (recv->dma_mode == BUFFER_FILL_MODE) - command |= (1 << 31); - - reg_write(recv->ohci, recv->ContextControlSet, command); - - /* match on specified tags */ - contextMatch = tag_mask << 28; - - if (iso->channel == -1) { - /* enable multichannel reception */ - reg_write(recv->ohci, recv->ContextControlSet, (1 << 28)); - } else { - /* listen on channel */ - contextMatch |= iso->channel; - } - - if (cycle != -1) { - u32 seconds; - - /* enable cycleMatch */ - reg_write(recv->ohci, recv->ContextControlSet, (1 << 29)); - - /* set starting cycle */ - cycle &= 0x1FFF; - - /* 'cycle' is only mod 8000, but we also need two 'seconds' bits - - just snarf them from the current time */ - seconds = reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer) >> 25; - - /* advance one second to give some extra time for DMA to start */ - seconds += 1; - - cycle |= (seconds & 3) << 13; - - contextMatch |= cycle << 12; - } - - if (sync != -1) { - /* set sync flag on first DMA descriptor */ - struct dma_cmd *cmd = &recv->block[recv->block_dma]; - cmd->control |= cpu_to_le32(DMA_CTL_WAIT); - - /* match sync field */ - contextMatch |= (sync&0xf)<<8; - } - - reg_write(recv->ohci, recv->ContextMatch, contextMatch); - - /* address of first descriptor block */ - command = dma_prog_region_offset_to_bus(&recv->prog, - recv->block_dma * sizeof(struct dma_cmd)); - command |= 1; /* Z=1 */ - - reg_write(recv->ohci, recv->CommandPtr, command); - - /* enable interrupts */ - reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskSet, 1 << recv->task.context); - - wmb(); - - /* run */ - reg_write(recv->ohci, recv->ContextControlSet, 0x8000); - - /* issue a dummy read of the cycle timer register to force - all PCI writes to be posted immediately */ - mb(); - reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer); - - /* check RUN */ - if (!(reg_read(recv->ohci, recv->ContextControlSet) & 0x8000)) { - PRINT(KERN_ERR, - "Error starting IR DMA (ContextControl 0x%08x)\n", - reg_read(recv->ohci, recv->ContextControlSet)); - return -1; - } - - return 0; -} - -static void ohci_iso_recv_release_block(struct ohci_iso_recv *recv, int block) -{ - /* re-use the DMA descriptor for the block */ - /* by linking the previous descriptor to it */ - - int next_i = block; - int prev_i = (next_i == 0) ? (recv->nblocks - 1) : (next_i - 1); - - struct dma_cmd *next = &recv->block[next_i]; - struct dma_cmd *prev = &recv->block[prev_i]; - - /* ignore out-of-range requests */ - if ((block < 0) || (block > recv->nblocks)) - return; - - /* 'next' becomes the new end of the DMA chain, - so disable branch and enable interrupt */ - next->branchAddress = 0; - next->control |= cpu_to_le32(3 << 20); - next->status = cpu_to_le32(recv->buf_stride); - - /* link prev to next */ - prev->branchAddress = cpu_to_le32(dma_prog_region_offset_to_bus(&recv->prog, - sizeof(struct dma_cmd) * next_i) - | 1); /* Z=1 */ - - /* disable interrupt on previous DMA descriptor, except at intervals */ - if ((prev_i % recv->block_irq_interval) == 0) { - prev->control |= cpu_to_le32(3 << 20); /* enable interrupt */ - } else { - prev->control &= cpu_to_le32(~(3<<20)); /* disable interrupt */ - } - wmb(); - - /* wake up DMA in case it fell asleep */ - reg_write(recv->ohci, recv->ContextControlSet, (1 << 12)); -} - -static void ohci_iso_recv_bufferfill_release(struct ohci_iso_recv *recv, - struct hpsb_iso_packet_info *info) -{ - /* release the memory where the packet was */ - recv->released_bytes += info->total_len; - - /* have we released enough memory for one block? */ - while (recv->released_bytes > recv->buf_stride) { - ohci_iso_recv_release_block(recv, recv->block_reader); - recv->block_reader = (recv->block_reader + 1) % recv->nblocks; - recv->released_bytes -= recv->buf_stride; - } -} - -static inline void ohci_iso_recv_release(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info) -{ - struct ohci_iso_recv *recv = iso->hostdata; - if (recv->dma_mode == BUFFER_FILL_MODE) { - ohci_iso_recv_bufferfill_release(recv, info); - } else { - ohci_iso_recv_release_block(recv, info - iso->infos); - } -} - -/* parse all packets from blocks that have been fully received */ -static void ohci_iso_recv_bufferfill_parse(struct hpsb_iso *iso, struct ohci_iso_recv *recv) -{ - int wake = 0; - int runaway = 0; - struct ti_ohci *ohci = recv->ohci; - - while (1) { - /* we expect the next parsable packet to begin at recv->dma_offset */ - /* note: packet layout is as shown in section 10.6.1.1 of the OHCI spec */ - - unsigned int offset; - unsigned short len, cycle, total_len; - unsigned char channel, tag, sy; - - unsigned char *p = iso->data_buf.kvirt; - - unsigned int this_block = recv->dma_offset/recv->buf_stride; - - /* don't loop indefinitely */ - if (runaway++ > 100000) { - atomic_inc(&iso->overflows); - PRINT(KERN_ERR, - "IR DMA error - Runaway during buffer parsing!\n"); - break; - } - - /* stop parsing once we arrive at block_dma (i.e. don't get ahead of DMA) */ - if (this_block == recv->block_dma) - break; - - wake = 1; - - /* parse data length, tag, channel, and sy */ - - /* note: we keep our own local copies of 'len' and 'offset' - so the user can't mess with them by poking in the mmap area */ - - len = p[recv->dma_offset+2] | (p[recv->dma_offset+3] << 8); - - if (len > 4096) { - PRINT(KERN_ERR, - "IR DMA error - bogus 'len' value %u\n", len); - } - - channel = p[recv->dma_offset+1] & 0x3F; - tag = p[recv->dma_offset+1] >> 6; - sy = p[recv->dma_offset+0] & 0xF; - - /* advance to data payload */ - recv->dma_offset += 4; - - /* check for wrap-around */ - if (recv->dma_offset >= recv->buf_stride*recv->nblocks) { - recv->dma_offset -= recv->buf_stride*recv->nblocks; - } - - /* dma_offset now points to the first byte of the data payload */ - offset = recv->dma_offset; - - /* advance to xferStatus/timeStamp */ - recv->dma_offset += len; - - total_len = len + 8; /* 8 bytes header+trailer in OHCI packet */ - /* payload is padded to 4 bytes */ - if (len % 4) { - recv->dma_offset += 4 - (len%4); - total_len += 4 - (len%4); - } - - /* check for wrap-around */ - if (recv->dma_offset >= recv->buf_stride*recv->nblocks) { - /* uh oh, the packet data wraps from the last - to the first DMA block - make the packet - contiguous by copying its "tail" into the - guard page */ - - int guard_off = recv->buf_stride*recv->nblocks; - int tail_len = len - (guard_off - offset); - - if (tail_len > 0 && tail_len < recv->buf_stride) { - memcpy(iso->data_buf.kvirt + guard_off, - iso->data_buf.kvirt, - tail_len); - } - - recv->dma_offset -= recv->buf_stride*recv->nblocks; - } - - /* parse timestamp */ - cycle = p[recv->dma_offset+0] | (p[recv->dma_offset+1]<<8); - cycle &= 0x1FFF; - - /* advance to next packet */ - recv->dma_offset += 4; - - /* check for wrap-around */ - if (recv->dma_offset >= recv->buf_stride*recv->nblocks) { - recv->dma_offset -= recv->buf_stride*recv->nblocks; - } - - hpsb_iso_packet_received(iso, offset, len, total_len, cycle, channel, tag, sy); - } - - if (wake) - hpsb_iso_wake(iso); -} - -static void ohci_iso_recv_bufferfill_task(struct hpsb_iso *iso, struct ohci_iso_recv *recv) -{ - int loop; - struct ti_ohci *ohci = recv->ohci; - - /* loop over all blocks */ - for (loop = 0; loop < recv->nblocks; loop++) { - - /* check block_dma to see if it's done */ - struct dma_cmd *im = &recv->block[recv->block_dma]; - - /* check the DMA descriptor for new writes to xferStatus */ - u16 xferstatus = le32_to_cpu(im->status) >> 16; - - /* rescount is the number of bytes *remaining to be written* in the block */ - u16 rescount = le32_to_cpu(im->status) & 0xFFFF; - - unsigned char event = xferstatus & 0x1F; - - if (!event) { - /* nothing has happened to this block yet */ - break; - } - - if (event != 0x11) { - atomic_inc(&iso->overflows); - PRINT(KERN_ERR, - "IR DMA error - OHCI error code 0x%02x\n", event); - } - - if (rescount != 0) { - /* the card is still writing to this block; - we can't touch it until it's done */ - break; - } - - /* OK, the block is finished... */ - - /* sync our view of the block */ - dma_region_sync_for_cpu(&iso->data_buf, recv->block_dma*recv->buf_stride, recv->buf_stride); - - /* reset the DMA descriptor */ - im->status = recv->buf_stride; - - /* advance block_dma */ - recv->block_dma = (recv->block_dma + 1) % recv->nblocks; - - if ((recv->block_dma+1) % recv->nblocks == recv->block_reader) { - atomic_inc(&iso->overflows); - DBGMSG("ISO reception overflow - " - "ran out of DMA blocks"); - } - } - - /* parse any packets that have arrived */ - ohci_iso_recv_bufferfill_parse(iso, recv); -} - -static void ohci_iso_recv_packetperbuf_task(struct hpsb_iso *iso, struct ohci_iso_recv *recv) -{ - int count; - int wake = 0; - struct ti_ohci *ohci = recv->ohci; - - /* loop over the entire buffer */ - for (count = 0; count < recv->nblocks; count++) { - u32 packet_len = 0; - - /* pointer to the DMA descriptor */ - struct dma_cmd *il = ((struct dma_cmd*) recv->prog.kvirt) + iso->pkt_dma; - - /* check the DMA descriptor for new writes to xferStatus */ - u16 xferstatus = le32_to_cpu(il->status) >> 16; - u16 rescount = le32_to_cpu(il->status) & 0xFFFF; - - unsigned char event = xferstatus & 0x1F; - - if (!event) { - /* this packet hasn't come in yet; we are done for now */ - goto out; - } - - if (event == 0x11) { - /* packet received successfully! */ - - /* rescount is the number of bytes *remaining* in the packet buffer, - after the packet was written */ - packet_len = recv->buf_stride - rescount; - - } else if (event == 0x02) { - PRINT(KERN_ERR, "IR DMA error - packet too long for buffer\n"); - } else if (event) { - PRINT(KERN_ERR, "IR DMA error - OHCI error code 0x%02x\n", event); - } - - /* sync our view of the buffer */ - dma_region_sync_for_cpu(&iso->data_buf, iso->pkt_dma * recv->buf_stride, recv->buf_stride); - - /* record the per-packet info */ - { - /* iso header is 8 bytes ahead of the data payload */ - unsigned char *hdr; - - unsigned int offset; - unsigned short cycle; - unsigned char channel, tag, sy; - - offset = iso->pkt_dma * recv->buf_stride; - hdr = iso->data_buf.kvirt + offset; - - /* skip iso header */ - offset += 8; - packet_len -= 8; - - cycle = (hdr[0] | (hdr[1] << 8)) & 0x1FFF; - channel = hdr[5] & 0x3F; - tag = hdr[5] >> 6; - sy = hdr[4] & 0xF; - - hpsb_iso_packet_received(iso, offset, packet_len, - recv->buf_stride, cycle, channel, tag, sy); - } - - /* reset the DMA descriptor */ - il->status = recv->buf_stride; - - wake = 1; - recv->block_dma = iso->pkt_dma; - } - -out: - if (wake) - hpsb_iso_wake(iso); -} - -static void ohci_iso_recv_task(unsigned long data) -{ - struct hpsb_iso *iso = (struct hpsb_iso*) data; - struct ohci_iso_recv *recv = iso->hostdata; - - if (recv->dma_mode == BUFFER_FILL_MODE) - ohci_iso_recv_bufferfill_task(iso, recv); - else - ohci_iso_recv_packetperbuf_task(iso, recv); -} - -/*********************************** - * rawiso ISO transmission * - ***********************************/ - -struct ohci_iso_xmit { - struct ti_ohci *ohci; - struct dma_prog_region prog; - struct ohci1394_iso_tasklet task; - int task_active; - int last_cycle; - atomic_t skips; - - u32 ContextControlSet; - u32 ContextControlClear; - u32 CommandPtr; -}; - -/* transmission DMA program: - one OUTPUT_MORE_IMMEDIATE for the IT header - one OUTPUT_LAST for the buffer data */ - -struct iso_xmit_cmd { - struct dma_cmd output_more_immediate; - u8 iso_hdr[8]; - u32 unused[2]; - struct dma_cmd output_last; -}; - -static int ohci_iso_xmit_init(struct hpsb_iso *iso); -static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle); -static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso); -static void ohci_iso_xmit_task(unsigned long data); - -static int ohci_iso_xmit_init(struct hpsb_iso *iso) -{ - struct ohci_iso_xmit *xmit; - unsigned int prog_size; - int ctx; - int ret = -ENOMEM; - - xmit = kmalloc(sizeof(*xmit), GFP_KERNEL); - if (!xmit) - return -ENOMEM; - - iso->hostdata = xmit; - xmit->ohci = iso->host->hostdata; - xmit->task_active = 0; - xmit->last_cycle = -1; - atomic_set(&iso->skips, 0); - - dma_prog_region_init(&xmit->prog); - - prog_size = sizeof(struct iso_xmit_cmd) * iso->buf_packets; - - if (dma_prog_region_alloc(&xmit->prog, prog_size, xmit->ohci->dev)) - goto err; - - ohci1394_init_iso_tasklet(&xmit->task, OHCI_ISO_TRANSMIT, - ohci_iso_xmit_task, (unsigned long) iso); - - if (ohci1394_register_iso_tasklet(xmit->ohci, &xmit->task) < 0) { - ret = -EBUSY; - goto err; - } - - xmit->task_active = 1; - - /* xmit context registers are spaced 16 bytes apart */ - ctx = xmit->task.context; - xmit->ContextControlSet = OHCI1394_IsoXmitContextControlSet + 16 * ctx; - xmit->ContextControlClear = OHCI1394_IsoXmitContextControlClear + 16 * ctx; - xmit->CommandPtr = OHCI1394_IsoXmitCommandPtr + 16 * ctx; - - return 0; - -err: - ohci_iso_xmit_shutdown(iso); - return ret; -} - -static void ohci_iso_xmit_stop(struct hpsb_iso *iso) -{ - struct ohci_iso_xmit *xmit = iso->hostdata; - struct ti_ohci *ohci = xmit->ohci; - - /* disable interrupts */ - reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskClear, 1 << xmit->task.context); - - /* halt DMA */ - if (ohci1394_stop_context(xmit->ohci, xmit->ContextControlClear, NULL)) { - /* XXX the DMA context will lock up if you try to send too much data! */ - PRINT(KERN_ERR, - "you probably exceeded the OHCI card's bandwidth limit - " - "reload the module and reduce xmit bandwidth"); - } -} - -static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso) -{ - struct ohci_iso_xmit *xmit = iso->hostdata; - - if (xmit->task_active) { - ohci_iso_xmit_stop(iso); - ohci1394_unregister_iso_tasklet(xmit->ohci, &xmit->task); - xmit->task_active = 0; - } - - dma_prog_region_free(&xmit->prog); - kfree(xmit); - iso->hostdata = NULL; -} - -static void ohci_iso_xmit_task(unsigned long data) -{ - struct hpsb_iso *iso = (struct hpsb_iso*) data; - struct ohci_iso_xmit *xmit = iso->hostdata; - struct ti_ohci *ohci = xmit->ohci; - int wake = 0; - int count; - - /* check the whole buffer if necessary, starting at pkt_dma */ - for (count = 0; count < iso->buf_packets; count++) { - int cycle; - - /* DMA descriptor */ - struct iso_xmit_cmd *cmd = dma_region_i(&xmit->prog, struct iso_xmit_cmd, iso->pkt_dma); - - /* check for new writes to xferStatus */ - u16 xferstatus = le32_to_cpu(cmd->output_last.status) >> 16; - u8 event = xferstatus & 0x1F; - - if (!event) { - /* packet hasn't been sent yet; we are done for now */ - break; - } - - if (event != 0x11) - PRINT(KERN_ERR, - "IT DMA error - OHCI error code 0x%02x\n", event); - - /* at least one packet went out, so wake up the writer */ - wake = 1; - - /* parse cycle */ - cycle = le32_to_cpu(cmd->output_last.status) & 0x1FFF; - - if (xmit->last_cycle > -1) { - int cycle_diff = cycle - xmit->last_cycle; - int skip; - - /* unwrap */ - if (cycle_diff < 0) { - cycle_diff += 8000; - if (cycle_diff < 0) - PRINT(KERN_ERR, "bogus cycle diff %d\n", - cycle_diff); - } - - skip = cycle_diff - 1; - if (skip > 0) { - DBGMSG("skipped %d cycles without packet loss", skip); - atomic_add(skip, &iso->skips); - } - } - xmit->last_cycle = cycle; - - /* tell the subsystem the packet has gone out */ - hpsb_iso_packet_sent(iso, cycle, event != 0x11); - - /* reset the DMA descriptor for next time */ - cmd->output_last.status = 0; - } - - if (wake) - hpsb_iso_wake(iso); -} - -static int ohci_iso_xmit_queue(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info) -{ - struct ohci_iso_xmit *xmit = iso->hostdata; - struct ti_ohci *ohci = xmit->ohci; - - int next_i, prev_i; - struct iso_xmit_cmd *next, *prev; - - unsigned int offset; - unsigned short len; - unsigned char tag, sy; - - /* check that the packet doesn't cross a page boundary - (we could allow this if we added OUTPUT_MORE descriptor support) */ - if (cross_bound(info->offset, info->len)) { - PRINT(KERN_ERR, - "rawiso xmit: packet %u crosses a page boundary", - iso->first_packet); - return -EINVAL; - } - - offset = info->offset; - len = info->len; - tag = info->tag; - sy = info->sy; - - /* sync up the card's view of the buffer */ - dma_region_sync_for_device(&iso->data_buf, offset, len); - - /* append first_packet to the DMA chain */ - /* by linking the previous descriptor to it */ - /* (next will become the new end of the DMA chain) */ - - next_i = iso->first_packet; - prev_i = (next_i == 0) ? (iso->buf_packets - 1) : (next_i - 1); - - next = dma_region_i(&xmit->prog, struct iso_xmit_cmd, next_i); - prev = dma_region_i(&xmit->prog, struct iso_xmit_cmd, prev_i); - - /* set up the OUTPUT_MORE_IMMEDIATE descriptor */ - memset(next, 0, sizeof(struct iso_xmit_cmd)); - next->output_more_immediate.control = cpu_to_le32(0x02000008); - - /* ISO packet header is embedded in the OUTPUT_MORE_IMMEDIATE */ - - /* tcode = 0xA, and sy */ - next->iso_hdr[0] = 0xA0 | (sy & 0xF); - - /* tag and channel number */ - next->iso_hdr[1] = (tag << 6) | (iso->channel & 0x3F); - - /* transmission speed */ - next->iso_hdr[2] = iso->speed & 0x7; - - /* payload size */ - next->iso_hdr[6] = len & 0xFF; - next->iso_hdr[7] = len >> 8; - - /* set up the OUTPUT_LAST */ - next->output_last.control = cpu_to_le32(1 << 28); - next->output_last.control |= cpu_to_le32(1 << 27); /* update timeStamp */ - next->output_last.control |= cpu_to_le32(3 << 20); /* want interrupt */ - next->output_last.control |= cpu_to_le32(3 << 18); /* enable branch */ - next->output_last.control |= cpu_to_le32(len); - - /* payload bus address */ - next->output_last.address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, offset)); - - /* leave branchAddress at zero for now */ - - /* re-write the previous DMA descriptor to chain to this one */ - - /* set prev branch address to point to next (Z=3) */ - prev->output_last.branchAddress = cpu_to_le32( - dma_prog_region_offset_to_bus(&xmit->prog, sizeof(struct iso_xmit_cmd) * next_i) | 3); - - /* - * Link the skip address to this descriptor itself. This causes a - * context to skip a cycle whenever lost cycles or FIFO overruns occur, - * without dropping the data at that point the application should then - * decide whether this is an error condition or not. Some protocols - * can deal with this by dropping some rate-matching padding packets. - */ - next->output_more_immediate.branchAddress = - prev->output_last.branchAddress; - - /* disable interrupt, unless required by the IRQ interval */ - if (prev_i % iso->irq_interval) { - prev->output_last.control &= cpu_to_le32(~(3 << 20)); /* no interrupt */ - } else { - prev->output_last.control |= cpu_to_le32(3 << 20); /* enable interrupt */ - } - - wmb(); - - /* wake DMA in case it is sleeping */ - reg_write(xmit->ohci, xmit->ContextControlSet, 1 << 12); - - /* issue a dummy read of the cycle timer to force all PCI - writes to be posted immediately */ - mb(); - reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer); - - return 0; -} - -static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle) -{ - struct ohci_iso_xmit *xmit = iso->hostdata; - struct ti_ohci *ohci = xmit->ohci; - - /* clear out the control register */ - reg_write(xmit->ohci, xmit->ContextControlClear, 0xFFFFFFFF); - wmb(); - - /* address and length of first descriptor block (Z=3) */ - reg_write(xmit->ohci, xmit->CommandPtr, - dma_prog_region_offset_to_bus(&xmit->prog, iso->pkt_dma * sizeof(struct iso_xmit_cmd)) | 3); - - /* cycle match */ - if (cycle != -1) { - u32 start = cycle & 0x1FFF; - - /* 'cycle' is only mod 8000, but we also need two 'seconds' bits - - just snarf them from the current time */ - u32 seconds = reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer) >> 25; - - /* advance one second to give some extra time for DMA to start */ - seconds += 1; - - start |= (seconds & 3) << 13; - - reg_write(xmit->ohci, xmit->ContextControlSet, 0x80000000 | (start << 16)); - } - - /* enable interrupts */ - reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskSet, 1 << xmit->task.context); - - /* run */ - reg_write(xmit->ohci, xmit->ContextControlSet, 0x8000); - mb(); - - /* wait 100 usec to give the card time to go active */ - udelay(100); - - /* check the RUN bit */ - if (!(reg_read(xmit->ohci, xmit->ContextControlSet) & 0x8000)) { - PRINT(KERN_ERR, "Error starting IT DMA (ContextControl 0x%08x)\n", - reg_read(xmit->ohci, xmit->ContextControlSet)); - return -1; - } - - return 0; -} - -static int ohci_isoctl(struct hpsb_iso *iso, enum isoctl_cmd cmd, unsigned long arg) -{ - - switch(cmd) { - case XMIT_INIT: - return ohci_iso_xmit_init(iso); - case XMIT_START: - return ohci_iso_xmit_start(iso, arg); - case XMIT_STOP: - ohci_iso_xmit_stop(iso); - return 0; - case XMIT_QUEUE: - return ohci_iso_xmit_queue(iso, (struct hpsb_iso_packet_info*) arg); - case XMIT_SHUTDOWN: - ohci_iso_xmit_shutdown(iso); - return 0; - - case RECV_INIT: - return ohci_iso_recv_init(iso); - case RECV_START: { - int *args = (int*) arg; - return ohci_iso_recv_start(iso, args[0], args[1], args[2]); - } - case RECV_STOP: - ohci_iso_recv_stop(iso); - return 0; - case RECV_RELEASE: - ohci_iso_recv_release(iso, (struct hpsb_iso_packet_info*) arg); - return 0; - case RECV_FLUSH: - ohci_iso_recv_task((unsigned long) iso); - return 0; - case RECV_SHUTDOWN: - ohci_iso_recv_shutdown(iso); - return 0; - case RECV_LISTEN_CHANNEL: - ohci_iso_recv_change_channel(iso, arg, 1); - return 0; - case RECV_UNLISTEN_CHANNEL: - ohci_iso_recv_change_channel(iso, arg, 0); - return 0; - case RECV_SET_CHANNEL_MASK: - ohci_iso_recv_set_channel_mask(iso, *((u64*) arg)); - return 0; - - default: - PRINT_G(KERN_ERR, "ohci_isoctl cmd %d not implemented yet", - cmd); - break; - } - return -EINVAL; -} - -/*************************************** - * IEEE-1394 functionality section END * - ***************************************/ - - -/******************************************************** - * Global stuff (interrupt handler, init/shutdown code) * - ********************************************************/ - -static void dma_trm_reset(struct dma_trm_ctx *d) -{ - unsigned long flags; - LIST_HEAD(packet_list); - struct ti_ohci *ohci = d->ohci; - struct hpsb_packet *packet, *ptmp; - - ohci1394_stop_context(ohci, d->ctrlClear, NULL); - - /* Lock the context, reset it and release it. Move the packets - * that were pending in the context to packet_list and free - * them after releasing the lock. */ - - spin_lock_irqsave(&d->lock, flags); - - list_splice_init(&d->fifo_list, &packet_list); - list_splice_init(&d->pending_list, &packet_list); - - d->branchAddrPtr = NULL; - d->sent_ind = d->prg_ind; - d->free_prgs = d->num_desc; - - spin_unlock_irqrestore(&d->lock, flags); - - if (list_empty(&packet_list)) - return; - - PRINT(KERN_INFO, "AT dma reset ctx=%d, aborting transmission", d->ctx); - - /* Now process subsystem callbacks for the packets from this - * context. */ - list_for_each_entry_safe(packet, ptmp, &packet_list, driver_list) { - list_del_init(&packet->driver_list); - hpsb_packet_sent(ohci->host, packet, ACKX_ABORTED); - } -} - -static void ohci_schedule_iso_tasklets(struct ti_ohci *ohci, - quadlet_t rx_event, - quadlet_t tx_event) -{ - struct ohci1394_iso_tasklet *t; - unsigned long mask; - unsigned long flags; - - spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags); - - list_for_each_entry(t, &ohci->iso_tasklet_list, link) { - mask = 1 << t->context; - - if (t->type == OHCI_ISO_TRANSMIT) { - if (tx_event & mask) - tasklet_schedule(&t->tasklet); - } else { - /* OHCI_ISO_RECEIVE or OHCI_ISO_MULTICHANNEL_RECEIVE */ - if (rx_event & mask) - tasklet_schedule(&t->tasklet); - } - } - - spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags); -} - -static irqreturn_t ohci_irq_handler(int irq, void *dev_id) -{ - quadlet_t event, node_id; - struct ti_ohci *ohci = (struct ti_ohci *)dev_id; - struct hpsb_host *host = ohci->host; - int phyid = -1, isroot = 0; - unsigned long flags; - - /* Read and clear the interrupt event register. Don't clear - * the busReset event, though. This is done when we get the - * selfIDComplete interrupt. */ - spin_lock_irqsave(&ohci->event_lock, flags); - event = reg_read(ohci, OHCI1394_IntEventClear); - reg_write(ohci, OHCI1394_IntEventClear, event & ~OHCI1394_busReset); - spin_unlock_irqrestore(&ohci->event_lock, flags); - - if (!event) - return IRQ_NONE; - - /* If event is ~(u32)0 cardbus card was ejected. In this case - * we just return, and clean up in the ohci1394_pci_remove - * function. */ - if (event == ~(u32) 0) { - DBGMSG("Device removed."); - return IRQ_NONE; - } - - DBGMSG("IntEvent: %08x", event); - - if (event & OHCI1394_unrecoverableError) { - int ctx; - PRINT(KERN_ERR, "Unrecoverable error!"); - - if (reg_read(ohci, OHCI1394_AsReqTrContextControlSet) & 0x800) - PRINT(KERN_ERR, "Async Req Tx Context died: " - "ctrl[%08x] cmdptr[%08x]", - reg_read(ohci, OHCI1394_AsReqTrContextControlSet), - reg_read(ohci, OHCI1394_AsReqTrCommandPtr)); - - if (reg_read(ohci, OHCI1394_AsRspTrContextControlSet) & 0x800) - PRINT(KERN_ERR, "Async Rsp Tx Context died: " - "ctrl[%08x] cmdptr[%08x]", - reg_read(ohci, OHCI1394_AsRspTrContextControlSet), - reg_read(ohci, OHCI1394_AsRspTrCommandPtr)); - - if (reg_read(ohci, OHCI1394_AsReqRcvContextControlSet) & 0x800) - PRINT(KERN_ERR, "Async Req Rcv Context died: " - "ctrl[%08x] cmdptr[%08x]", - reg_read(ohci, OHCI1394_AsReqRcvContextControlSet), - reg_read(ohci, OHCI1394_AsReqRcvCommandPtr)); - - if (reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) & 0x800) - PRINT(KERN_ERR, "Async Rsp Rcv Context died: " - "ctrl[%08x] cmdptr[%08x]", - reg_read(ohci, OHCI1394_AsRspRcvContextControlSet), - reg_read(ohci, OHCI1394_AsRspRcvCommandPtr)); - - for (ctx = 0; ctx < ohci->nb_iso_xmit_ctx; ctx++) { - if (reg_read(ohci, OHCI1394_IsoXmitContextControlSet + (16 * ctx)) & 0x800) - PRINT(KERN_ERR, "Iso Xmit %d Context died: " - "ctrl[%08x] cmdptr[%08x]", ctx, - reg_read(ohci, OHCI1394_IsoXmitContextControlSet + (16 * ctx)), - reg_read(ohci, OHCI1394_IsoXmitCommandPtr + (16 * ctx))); - } - - for (ctx = 0; ctx < ohci->nb_iso_rcv_ctx; ctx++) { - if (reg_read(ohci, OHCI1394_IsoRcvContextControlSet + (32 * ctx)) & 0x800) - PRINT(KERN_ERR, "Iso Recv %d Context died: " - "ctrl[%08x] cmdptr[%08x] match[%08x]", ctx, - reg_read(ohci, OHCI1394_IsoRcvContextControlSet + (32 * ctx)), - reg_read(ohci, OHCI1394_IsoRcvCommandPtr + (32 * ctx)), - reg_read(ohci, OHCI1394_IsoRcvContextMatch + (32 * ctx))); - } - - event &= ~OHCI1394_unrecoverableError; - } - if (event & OHCI1394_postedWriteErr) { - PRINT(KERN_ERR, "physical posted write error"); - /* no recovery strategy yet, had to involve protocol drivers */ - event &= ~OHCI1394_postedWriteErr; - } - if (event & OHCI1394_cycleTooLong) { - if(printk_ratelimit()) - PRINT(KERN_WARNING, "isochronous cycle too long"); - else - DBGMSG("OHCI1394_cycleTooLong"); - reg_write(ohci, OHCI1394_LinkControlSet, - OHCI1394_LinkControl_CycleMaster); - event &= ~OHCI1394_cycleTooLong; - } - if (event & OHCI1394_cycleInconsistent) { - /* We subscribe to the cycleInconsistent event only to - * clear the corresponding event bit... otherwise, - * isochronous cycleMatch DMA won't work. */ - DBGMSG("OHCI1394_cycleInconsistent"); - event &= ~OHCI1394_cycleInconsistent; - } - if (event & OHCI1394_busReset) { - /* The busReset event bit can't be cleared during the - * selfID phase, so we disable busReset interrupts, to - * avoid burying the cpu in interrupt requests. */ - spin_lock_irqsave(&ohci->event_lock, flags); - reg_write(ohci, OHCI1394_IntMaskClear, OHCI1394_busReset); - - if (ohci->check_busreset) { - int loop_count = 0; - - udelay(10); - - while (reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) { - reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset); - - spin_unlock_irqrestore(&ohci->event_lock, flags); - udelay(10); - spin_lock_irqsave(&ohci->event_lock, flags); - - /* The loop counter check is to prevent the driver - * from remaining in this state forever. For the - * initial bus reset, the loop continues for ever - * and the system hangs, until some device is plugged-in - * or out manually into a port! The forced reset seems - * to solve this problem. This mainly effects nForce2. */ - if (loop_count > 10000) { - ohci_devctl(host, RESET_BUS, LONG_RESET); - DBGMSG("Detected bus-reset loop. Forced a bus reset!"); - loop_count = 0; - } - - loop_count++; - } - } - spin_unlock_irqrestore(&ohci->event_lock, flags); - if (!host->in_bus_reset) { - DBGMSG("irq_handler: Bus reset requested"); - - /* Subsystem call */ - hpsb_bus_reset(ohci->host); - } - event &= ~OHCI1394_busReset; - } - if (event & OHCI1394_reqTxComplete) { - struct dma_trm_ctx *d = &ohci->at_req_context; - DBGMSG("Got reqTxComplete interrupt " - "status=0x%08X", reg_read(ohci, d->ctrlSet)); - if (reg_read(ohci, d->ctrlSet) & 0x800) - ohci1394_stop_context(ohci, d->ctrlClear, - "reqTxComplete"); - else - dma_trm_tasklet((unsigned long)d); - //tasklet_schedule(&d->task); - event &= ~OHCI1394_reqTxComplete; - } - if (event & OHCI1394_respTxComplete) { - struct dma_trm_ctx *d = &ohci->at_resp_context; - DBGMSG("Got respTxComplete interrupt " - "status=0x%08X", reg_read(ohci, d->ctrlSet)); - if (reg_read(ohci, d->ctrlSet) & 0x800) - ohci1394_stop_context(ohci, d->ctrlClear, - "respTxComplete"); - else - tasklet_schedule(&d->task); - event &= ~OHCI1394_respTxComplete; - } - if (event & OHCI1394_RQPkt) { - struct dma_rcv_ctx *d = &ohci->ar_req_context; - DBGMSG("Got RQPkt interrupt status=0x%08X", - reg_read(ohci, d->ctrlSet)); - if (reg_read(ohci, d->ctrlSet) & 0x800) - ohci1394_stop_context(ohci, d->ctrlClear, "RQPkt"); - else - tasklet_schedule(&d->task); - event &= ~OHCI1394_RQPkt; - } - if (event & OHCI1394_RSPkt) { - struct dma_rcv_ctx *d = &ohci->ar_resp_context; - DBGMSG("Got RSPkt interrupt status=0x%08X", - reg_read(ohci, d->ctrlSet)); - if (reg_read(ohci, d->ctrlSet) & 0x800) - ohci1394_stop_context(ohci, d->ctrlClear, "RSPkt"); - else - tasklet_schedule(&d->task); - event &= ~OHCI1394_RSPkt; - } - if (event & OHCI1394_isochRx) { - quadlet_t rx_event; - - rx_event = reg_read(ohci, OHCI1394_IsoRecvIntEventSet); - reg_write(ohci, OHCI1394_IsoRecvIntEventClear, rx_event); - ohci_schedule_iso_tasklets(ohci, rx_event, 0); - event &= ~OHCI1394_isochRx; - } - if (event & OHCI1394_isochTx) { - quadlet_t tx_event; - - tx_event = reg_read(ohci, OHCI1394_IsoXmitIntEventSet); - reg_write(ohci, OHCI1394_IsoXmitIntEventClear, tx_event); - ohci_schedule_iso_tasklets(ohci, 0, tx_event); - event &= ~OHCI1394_isochTx; - } - if (event & OHCI1394_selfIDComplete) { - if (host->in_bus_reset) { - node_id = reg_read(ohci, OHCI1394_NodeID); - - if (!(node_id & 0x80000000)) { - PRINT(KERN_ERR, - "SelfID received, but NodeID invalid " - "(probably new bus reset occurred): %08X", - node_id); - goto selfid_not_valid; - } - - phyid = node_id & 0x0000003f; - isroot = (node_id & 0x40000000) != 0; - - DBGMSG("SelfID interrupt received " - "(phyid %d, %s)", phyid, - (isroot ? "root" : "not root")); - - handle_selfid(ohci, host, phyid, isroot); - - /* Clear the bus reset event and re-enable the - * busReset interrupt. */ - spin_lock_irqsave(&ohci->event_lock, flags); - reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset); - reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset); - spin_unlock_irqrestore(&ohci->event_lock, flags); - - /* Turn on phys dma reception. - * - * TODO: Enable some sort of filtering management. - */ - if (phys_dma) { - reg_write(ohci, OHCI1394_PhyReqFilterHiSet, - 0xffffffff); - reg_write(ohci, OHCI1394_PhyReqFilterLoSet, - 0xffffffff); - } - - DBGMSG("PhyReqFilter=%08x%08x", - reg_read(ohci, OHCI1394_PhyReqFilterHiSet), - reg_read(ohci, OHCI1394_PhyReqFilterLoSet)); - - hpsb_selfid_complete(host, phyid, isroot); - } else - PRINT(KERN_ERR, - "SelfID received outside of bus reset sequence"); - -selfid_not_valid: - event &= ~OHCI1394_selfIDComplete; - } - - /* Make sure we handle everything, just in case we accidentally - * enabled an interrupt that we didn't write a handler for. */ - if (event) - PRINT(KERN_ERR, "Unhandled interrupt(s) 0x%08x", - event); - - return IRQ_HANDLED; -} - -/* Put the buffer back into the dma context */ -static void insert_dma_buffer(struct dma_rcv_ctx *d, int idx) -{ - struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); - DBGMSG("Inserting dma buf ctx=%d idx=%d", d->ctx, idx); - - d->prg_cpu[idx]->status = cpu_to_le32(d->buf_size); - d->prg_cpu[idx]->branchAddress &= le32_to_cpu(0xfffffff0); - idx = (idx + d->num_desc - 1 ) % d->num_desc; - d->prg_cpu[idx]->branchAddress |= le32_to_cpu(0x00000001); - - /* To avoid a race, ensure 1394 interface hardware sees the inserted - * context program descriptors before it sees the wakeup bit set. */ - wmb(); - - /* wake up the dma context if necessary */ - if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { - PRINT(KERN_INFO, - "Waking dma ctx=%d ... processing is probably too slow", - d->ctx); - } - - /* do this always, to avoid race condition */ - reg_write(ohci, d->ctrlSet, 0x1000); -} - -#define cond_le32_to_cpu(data, noswap) \ - (noswap ? data : le32_to_cpu(data)) - -static const int TCODE_SIZE[16] = {20, 0, 16, -1, 16, 20, 20, 0, - -1, 0, -1, 0, -1, -1, 16, -1}; - -/* - * Determine the length of a packet in the buffer - * Optimization suggested by Pascal Drolet - */ -static inline int packet_length(struct dma_rcv_ctx *d, int idx, - quadlet_t *buf_ptr, int offset, - unsigned char tcode, int noswap) -{ - int length = -1; - - if (d->type == DMA_CTX_ASYNC_REQ || d->type == DMA_CTX_ASYNC_RESP) { - length = TCODE_SIZE[tcode]; - if (length == 0) { - if (offset + 12 >= d->buf_size) { - length = (cond_le32_to_cpu(d->buf_cpu[(idx + 1) % d->num_desc] - [3 - ((d->buf_size - offset) >> 2)], noswap) >> 16); - } else { - length = (cond_le32_to_cpu(buf_ptr[3], noswap) >> 16); - } - length += 20; - } - } else if (d->type == DMA_CTX_ISO) { - /* Assumption: buffer fill mode with header/trailer */ - length = (cond_le32_to_cpu(buf_ptr[0], noswap) >> 16) + 8; - } - - if (length > 0 && length % 4) - length += 4 - (length % 4); - - return length; -} - -/* Tasklet that processes dma receive buffers */ -static void dma_rcv_tasklet (unsigned long data) -{ - struct dma_rcv_ctx *d = (struct dma_rcv_ctx*)data; - struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); - unsigned int split_left, idx, offset, rescount; - unsigned char tcode; - int length, bytes_left, ack; - unsigned long flags; - quadlet_t *buf_ptr; - char *split_ptr; - char msg[256]; - - spin_lock_irqsave(&d->lock, flags); - - idx = d->buf_ind; - offset = d->buf_offset; - buf_ptr = d->buf_cpu[idx] + offset/4; - - rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff; - bytes_left = d->buf_size - rescount - offset; - - while (bytes_left > 0) { - tcode = (cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming) >> 4) & 0xf; - - /* packet_length() will return < 4 for an error */ - length = packet_length(d, idx, buf_ptr, offset, tcode, ohci->no_swap_incoming); - - if (length < 4) { /* something is wrong */ - sprintf(msg,"Unexpected tcode 0x%x(0x%08x) in AR ctx=%d, length=%d", - tcode, cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming), - d->ctx, length); - ohci1394_stop_context(ohci, d->ctrlClear, msg); - spin_unlock_irqrestore(&d->lock, flags); - return; - } - - /* The first case is where we have a packet that crosses - * over more than one descriptor. The next case is where - * it's all in the first descriptor. */ - if ((offset + length) > d->buf_size) { - DBGMSG("Split packet rcv'd"); - if (length > d->split_buf_size) { - ohci1394_stop_context(ohci, d->ctrlClear, - "Split packet size exceeded"); - d->buf_ind = idx; - d->buf_offset = offset; - spin_unlock_irqrestore(&d->lock, flags); - return; - } - - if (le32_to_cpu(d->prg_cpu[(idx+1)%d->num_desc]->status) - == d->buf_size) { - /* Other part of packet not written yet. - * this should never happen I think - * anyway we'll get it on the next call. */ - PRINT(KERN_INFO, - "Got only half a packet!"); - d->buf_ind = idx; - d->buf_offset = offset; - spin_unlock_irqrestore(&d->lock, flags); - return; - } - - split_left = length; - split_ptr = (char *)d->spb; - memcpy(split_ptr,buf_ptr,d->buf_size-offset); - split_left -= d->buf_size-offset; - split_ptr += d->buf_size-offset; - insert_dma_buffer(d, idx); - idx = (idx+1) % d->num_desc; - buf_ptr = d->buf_cpu[idx]; - offset=0; - - while (split_left >= d->buf_size) { - memcpy(split_ptr,buf_ptr,d->buf_size); - split_ptr += d->buf_size; - split_left -= d->buf_size; - insert_dma_buffer(d, idx); - idx = (idx+1) % d->num_desc; - buf_ptr = d->buf_cpu[idx]; - } - - if (split_left > 0) { - memcpy(split_ptr, buf_ptr, split_left); - offset = split_left; - buf_ptr += offset/4; - } - } else { - DBGMSG("Single packet rcv'd"); - memcpy(d->spb, buf_ptr, length); - offset += length; - buf_ptr += length/4; - if (offset==d->buf_size) { - insert_dma_buffer(d, idx); - idx = (idx+1) % d->num_desc; - buf_ptr = d->buf_cpu[idx]; - offset=0; - } - } - - /* We get one phy packet to the async descriptor for each - * bus reset. We always ignore it. */ - if (tcode != OHCI1394_TCODE_PHY) { - if (!ohci->no_swap_incoming) - header_le32_to_cpu(d->spb, tcode); - DBGMSG("Packet received from node" - " %d ack=0x%02X spd=%d tcode=0x%X" - " length=%d ctx=%d tlabel=%d", - (d->spb[1]>>16)&0x3f, - (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f, - (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>21)&0x3, - tcode, length, d->ctx, - (d->spb[0]>>10)&0x3f); - - ack = (((cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f) - == 0x11) ? 1 : 0; - - hpsb_packet_received(ohci->host, d->spb, - length-4, ack); - } -#ifdef OHCI1394_DEBUG - else - PRINT (KERN_DEBUG, "Got phy packet ctx=%d ... discarded", - d->ctx); -#endif - - rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff; - - bytes_left = d->buf_size - rescount - offset; - - } - - d->buf_ind = idx; - d->buf_offset = offset; - - spin_unlock_irqrestore(&d->lock, flags); -} - -/* Bottom half that processes sent packets */ -static void dma_trm_tasklet (unsigned long data) -{ - struct dma_trm_ctx *d = (struct dma_trm_ctx*)data; - struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); - struct hpsb_packet *packet, *ptmp; - unsigned long flags; - u32 status, ack; - size_t datasize; - - spin_lock_irqsave(&d->lock, flags); - - list_for_each_entry_safe(packet, ptmp, &d->fifo_list, driver_list) { - datasize = packet->data_size; - if (datasize && packet->type != hpsb_raw) - status = le32_to_cpu( - d->prg_cpu[d->sent_ind]->end.status) >> 16; - else - status = le32_to_cpu( - d->prg_cpu[d->sent_ind]->begin.status) >> 16; - - if (status == 0) - /* this packet hasn't been sent yet*/ - break; - -#ifdef OHCI1394_DEBUG - if (datasize) - if (((le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf) == 0xa) - DBGMSG("Stream packet sent to channel %d tcode=0x%X " - "ack=0x%X spd=%d dataLength=%d ctx=%d", - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>8)&0x3f, - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf, - status&0x1f, (status>>5)&0x3, - le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])>>16, - d->ctx); - else - DBGMSG("Packet sent to node %d tcode=0x%X tLabel=" - "%d ack=0x%X spd=%d dataLength=%d ctx=%d", - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])>>16)&0x3f, - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf, - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>10)&0x3f, - status&0x1f, (status>>5)&0x3, - le32_to_cpu(d->prg_cpu[d->sent_ind]->data[3])>>16, - d->ctx); - else - DBGMSG("Packet sent to node %d tcode=0x%X tLabel=" - "%d ack=0x%X spd=%d data=0x%08X ctx=%d", - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1]) - >>16)&0x3f, - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0]) - >>4)&0xf, - (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0]) - >>10)&0x3f, - status&0x1f, (status>>5)&0x3, - le32_to_cpu(d->prg_cpu[d->sent_ind]->data[3]), - d->ctx); -#endif - - if (status & 0x10) { - ack = status & 0xf; - } else { - switch (status & 0x1f) { - case EVT_NO_STATUS: /* that should never happen */ - case EVT_RESERVED_A: /* that should never happen */ - case EVT_LONG_PACKET: /* that should never happen */ - PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f); - ack = ACKX_SEND_ERROR; - break; - case EVT_MISSING_ACK: - ack = ACKX_TIMEOUT; - break; - case EVT_UNDERRUN: - ack = ACKX_SEND_ERROR; - break; - case EVT_OVERRUN: /* that should never happen */ - PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f); - ack = ACKX_SEND_ERROR; - break; - case EVT_DESCRIPTOR_READ: - case EVT_DATA_READ: - case EVT_DATA_WRITE: - ack = ACKX_SEND_ERROR; - break; - case EVT_BUS_RESET: /* that should never happen */ - PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f); - ack = ACKX_SEND_ERROR; - break; - case EVT_TIMEOUT: - ack = ACKX_TIMEOUT; - break; - case EVT_TCODE_ERR: - ack = ACKX_SEND_ERROR; - break; - case EVT_RESERVED_B: /* that should never happen */ - case EVT_RESERVED_C: /* that should never happen */ - PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f); - ack = ACKX_SEND_ERROR; - break; - case EVT_UNKNOWN: - case EVT_FLUSHED: - ack = ACKX_SEND_ERROR; - break; - default: - PRINT(KERN_ERR, "Unhandled OHCI evt_* error 0x%x", status & 0x1f); - ack = ACKX_SEND_ERROR; - BUG(); - } - } - - list_del_init(&packet->driver_list); - hpsb_packet_sent(ohci->host, packet, ack); - - if (datasize) - pci_unmap_single(ohci->dev, - cpu_to_le32(d->prg_cpu[d->sent_ind]->end.address), - datasize, PCI_DMA_TODEVICE); - - d->sent_ind = (d->sent_ind+1)%d->num_desc; - d->free_prgs++; - } - - dma_trm_flush(ohci, d); - - spin_unlock_irqrestore(&d->lock, flags); -} - -static void free_dma_rcv_ctx(struct dma_rcv_ctx *d) -{ - int i; - struct ti_ohci *ohci = d->ohci; - - if (ohci == NULL) - return; - - DBGMSG("Freeing dma_rcv_ctx %d", d->ctx); - - if (d->buf_cpu) { - for (i=0; inum_desc; i++) - if (d->buf_cpu[i] && d->buf_bus[i]) - pci_free_consistent( - ohci->dev, d->buf_size, - d->buf_cpu[i], d->buf_bus[i]); - kfree(d->buf_cpu); - kfree(d->buf_bus); - } - if (d->prg_cpu) { - for (i=0; inum_desc; i++) - if (d->prg_cpu[i] && d->prg_bus[i]) - pci_pool_free(d->prg_pool, d->prg_cpu[i], - d->prg_bus[i]); - pci_pool_destroy(d->prg_pool); - kfree(d->prg_cpu); - kfree(d->prg_bus); - } - kfree(d->spb); - - /* Mark this context as freed. */ - d->ohci = NULL; -} - -static int -alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d, - enum context_type type, int ctx, int num_desc, - int buf_size, int split_buf_size, int context_base) -{ - int i, len; - static int num_allocs; - static char pool_name[20]; - - d->ohci = ohci; - d->type = type; - d->ctx = ctx; - - d->num_desc = num_desc; - d->buf_size = buf_size; - d->split_buf_size = split_buf_size; - - d->ctrlSet = 0; - d->ctrlClear = 0; - d->cmdPtr = 0; - - d->buf_cpu = kzalloc(d->num_desc * sizeof(*d->buf_cpu), GFP_ATOMIC); - d->buf_bus = kzalloc(d->num_desc * sizeof(*d->buf_bus), GFP_ATOMIC); - - if (d->buf_cpu == NULL || d->buf_bus == NULL) { - PRINT(KERN_ERR, "Failed to allocate %s", "DMA buffer"); - free_dma_rcv_ctx(d); - return -ENOMEM; - } - - d->prg_cpu = kzalloc(d->num_desc * sizeof(*d->prg_cpu), GFP_ATOMIC); - d->prg_bus = kzalloc(d->num_desc * sizeof(*d->prg_bus), GFP_ATOMIC); - - if (d->prg_cpu == NULL || d->prg_bus == NULL) { - PRINT(KERN_ERR, "Failed to allocate %s", "DMA prg"); - free_dma_rcv_ctx(d); - return -ENOMEM; - } - - d->spb = kmalloc(d->split_buf_size, GFP_ATOMIC); - - if (d->spb == NULL) { - PRINT(KERN_ERR, "Failed to allocate %s", "split buffer"); - free_dma_rcv_ctx(d); - return -ENOMEM; - } - - len = sprintf(pool_name, "ohci1394_rcv_prg"); - sprintf(pool_name+len, "%d", num_allocs); - d->prg_pool = pci_pool_create(pool_name, ohci->dev, - sizeof(struct dma_cmd), 4, 0); - if(d->prg_pool == NULL) - { - PRINT(KERN_ERR, "pci_pool_create failed for %s", pool_name); - free_dma_rcv_ctx(d); - return -ENOMEM; - } - num_allocs++; - - for (i=0; inum_desc; i++) { - d->buf_cpu[i] = pci_alloc_consistent(ohci->dev, - d->buf_size, - d->buf_bus+i); - - if (d->buf_cpu[i] != NULL) { - memset(d->buf_cpu[i], 0, d->buf_size); - } else { - PRINT(KERN_ERR, - "Failed to allocate %s", "DMA buffer"); - free_dma_rcv_ctx(d); - return -ENOMEM; - } - - d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, GFP_KERNEL, d->prg_bus+i); - - if (d->prg_cpu[i] != NULL) { - memset(d->prg_cpu[i], 0, sizeof(struct dma_cmd)); - } else { - PRINT(KERN_ERR, - "Failed to allocate %s", "DMA prg"); - free_dma_rcv_ctx(d); - return -ENOMEM; - } - } - - spin_lock_init(&d->lock); - - d->ctrlSet = context_base + OHCI1394_ContextControlSet; - d->ctrlClear = context_base + OHCI1394_ContextControlClear; - d->cmdPtr = context_base + OHCI1394_ContextCommandPtr; - - tasklet_init(&d->task, dma_rcv_tasklet, (unsigned long) d); - return 0; -} - -static void free_dma_trm_ctx(struct dma_trm_ctx *d) -{ - int i; - struct ti_ohci *ohci = d->ohci; - - if (ohci == NULL) - return; - - DBGMSG("Freeing dma_trm_ctx %d", d->ctx); - - if (d->prg_cpu) { - for (i=0; inum_desc; i++) - if (d->prg_cpu[i] && d->prg_bus[i]) - pci_pool_free(d->prg_pool, d->prg_cpu[i], - d->prg_bus[i]); - pci_pool_destroy(d->prg_pool); - kfree(d->prg_cpu); - kfree(d->prg_bus); - } - - /* Mark this context as freed. */ - d->ohci = NULL; -} - -static int -alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d, - enum context_type type, int ctx, int num_desc, - int context_base) -{ - int i, len; - static char pool_name[20]; - static int num_allocs=0; - - d->ohci = ohci; - d->type = type; - d->ctx = ctx; - d->num_desc = num_desc; - d->ctrlSet = 0; - d->ctrlClear = 0; - d->cmdPtr = 0; - - d->prg_cpu = kzalloc(d->num_desc * sizeof(*d->prg_cpu), GFP_KERNEL); - d->prg_bus = kzalloc(d->num_desc * sizeof(*d->prg_bus), GFP_KERNEL); - - if (d->prg_cpu == NULL || d->prg_bus == NULL) { - PRINT(KERN_ERR, "Failed to allocate %s", "AT DMA prg"); - free_dma_trm_ctx(d); - return -ENOMEM; - } - - len = sprintf(pool_name, "ohci1394_trm_prg"); - sprintf(pool_name+len, "%d", num_allocs); - d->prg_pool = pci_pool_create(pool_name, ohci->dev, - sizeof(struct at_dma_prg), 4, 0); - if (d->prg_pool == NULL) { - PRINT(KERN_ERR, "pci_pool_create failed for %s", pool_name); - free_dma_trm_ctx(d); - return -ENOMEM; - } - num_allocs++; - - for (i = 0; i < d->num_desc; i++) { - d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, GFP_KERNEL, d->prg_bus+i); - - if (d->prg_cpu[i] != NULL) { - memset(d->prg_cpu[i], 0, sizeof(struct at_dma_prg)); - } else { - PRINT(KERN_ERR, - "Failed to allocate %s", "AT DMA prg"); - free_dma_trm_ctx(d); - return -ENOMEM; - } - } - - spin_lock_init(&d->lock); - - /* initialize tasklet */ - d->ctrlSet = context_base + OHCI1394_ContextControlSet; - d->ctrlClear = context_base + OHCI1394_ContextControlClear; - d->cmdPtr = context_base + OHCI1394_ContextCommandPtr; - tasklet_init(&d->task, dma_trm_tasklet, (unsigned long)d); - return 0; -} - -static void ohci_set_hw_config_rom(struct hpsb_host *host, __be32 *config_rom) -{ - struct ti_ohci *ohci = host->hostdata; - - reg_write(ohci, OHCI1394_ConfigROMhdr, be32_to_cpu(config_rom[0])); - reg_write(ohci, OHCI1394_BusOptions, be32_to_cpu(config_rom[2])); - - memcpy(ohci->csr_config_rom_cpu, config_rom, OHCI_CONFIG_ROM_LEN); -} - - -static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg, - quadlet_t data, quadlet_t compare) -{ - struct ti_ohci *ohci = host->hostdata; - int i; - - reg_write(ohci, OHCI1394_CSRData, data); - reg_write(ohci, OHCI1394_CSRCompareData, compare); - reg_write(ohci, OHCI1394_CSRControl, reg & 0x3); - - for (i = 0; i < OHCI_LOOP_COUNT; i++) { - if (reg_read(ohci, OHCI1394_CSRControl) & 0x80000000) - break; - - mdelay(1); - } - - return reg_read(ohci, OHCI1394_CSRData); -} - -static struct hpsb_host_driver ohci1394_driver = { - .owner = THIS_MODULE, - .name = OHCI1394_DRIVER_NAME, - .set_hw_config_rom = ohci_set_hw_config_rom, - .transmit_packet = ohci_transmit, - .devctl = ohci_devctl, - .isoctl = ohci_isoctl, - .hw_csr_reg = ohci_hw_csr_reg, -}; - -/*********************************** - * PCI Driver Interface functions * - ***********************************/ - -#ifdef CONFIG_PPC_PMAC -static void ohci1394_pmac_on(struct pci_dev *dev) -{ - if (machine_is(powermac)) { - struct device_node *ofn = pci_device_to_OF_node(dev); - - if (ofn) { - pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, ofn, 0, 1); - pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 1); - } - } -} - -static void ohci1394_pmac_off(struct pci_dev *dev) -{ - if (machine_is(powermac)) { - struct device_node *ofn = pci_device_to_OF_node(dev); - - if (ofn) { - pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 0); - pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, ofn, 0, 0); - } - } -} -#else -#define ohci1394_pmac_on(dev) -#define ohci1394_pmac_off(dev) -#endif /* CONFIG_PPC_PMAC */ - -static int __devinit ohci1394_pci_probe(struct pci_dev *dev, - const struct pci_device_id *ent) -{ - struct hpsb_host *host; - struct ti_ohci *ohci; /* shortcut to currently handled device */ - resource_size_t ohci_base; - int err = -ENOMEM; - - ohci1394_pmac_on(dev); - if (pci_enable_device(dev)) { - PRINT_G(KERN_ERR, "Failed to enable OHCI hardware"); - err = -ENXIO; - goto err; - } - pci_set_master(dev); - - host = hpsb_alloc_host(&ohci1394_driver, sizeof(struct ti_ohci), &dev->dev); - if (!host) { - PRINT_G(KERN_ERR, "Failed to allocate %s", "host structure"); - goto err; - } - ohci = host->hostdata; - ohci->dev = dev; - ohci->host = host; - ohci->init_state = OHCI_INIT_ALLOC_HOST; - host->pdev = dev; - pci_set_drvdata(dev, ohci); - - /* We don't want hardware swapping */ - pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0); - - /* Some oddball Apple controllers do not order the selfid - * properly, so we make up for it here. */ -#ifndef __LITTLE_ENDIAN - /* XXX: Need a better way to check this. I'm wondering if we can - * read the values of the OHCI1394_PCI_HCI_Control and the - * noByteSwapData registers to see if they were not cleared to - * zero. Should this work? Obviously it's not defined what these - * registers will read when they aren't supported. Bleh! */ - if (dev->vendor == PCI_VENDOR_ID_APPLE && - dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) { - ohci->no_swap_incoming = 1; - ohci->selfid_swap = 0; - } else - ohci->selfid_swap = 1; -#endif - - -#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2_FW -#define PCI_DEVICE_ID_NVIDIA_NFORCE2_FW 0x006e -#endif - - /* These chipsets require a bit of extra care when checking after - * a busreset. */ - if ((dev->vendor == PCI_VENDOR_ID_APPLE && - dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) || - (dev->vendor == PCI_VENDOR_ID_NVIDIA && - dev->device == PCI_DEVICE_ID_NVIDIA_NFORCE2_FW)) - ohci->check_busreset = 1; - - /* We hardwire the MMIO length, since some CardBus adaptors - * fail to report the right length. Anyway, the ohci spec - * clearly says it's 2kb, so this shouldn't be a problem. */ - ohci_base = pci_resource_start(dev, 0); - if (pci_resource_len(dev, 0) < OHCI1394_REGISTER_SIZE) - PRINT(KERN_WARNING, "PCI resource length of 0x%llx too small!", - (unsigned long long)pci_resource_len(dev, 0)); - - if (!request_mem_region(ohci_base, OHCI1394_REGISTER_SIZE, - OHCI1394_DRIVER_NAME)) { - PRINT_G(KERN_ERR, "MMIO resource (0x%llx - 0x%llx) unavailable", - (unsigned long long)ohci_base, - (unsigned long long)ohci_base + OHCI1394_REGISTER_SIZE); - goto err; - } - ohci->init_state = OHCI_INIT_HAVE_MEM_REGION; - - ohci->registers = ioremap(ohci_base, OHCI1394_REGISTER_SIZE); - if (ohci->registers == NULL) { - PRINT_G(KERN_ERR, "Failed to remap registers"); - err = -ENXIO; - goto err; - } - ohci->init_state = OHCI_INIT_HAVE_IOMAPPING; - DBGMSG("Remapped memory spaces reg 0x%p", ohci->registers); - - /* csr_config rom allocation */ - ohci->csr_config_rom_cpu = - pci_alloc_consistent(ohci->dev, OHCI_CONFIG_ROM_LEN, - &ohci->csr_config_rom_bus); - if (ohci->csr_config_rom_cpu == NULL) { - PRINT_G(KERN_ERR, "Failed to allocate %s", "buffer config rom"); - goto err; - } - ohci->init_state = OHCI_INIT_HAVE_CONFIG_ROM_BUFFER; - - /* self-id dma buffer allocation */ - ohci->selfid_buf_cpu = - pci_alloc_consistent(ohci->dev, OHCI1394_SI_DMA_BUF_SIZE, - &ohci->selfid_buf_bus); - if (ohci->selfid_buf_cpu == NULL) { - PRINT_G(KERN_ERR, "Failed to allocate %s", "self-ID buffer"); - goto err; - } - ohci->init_state = OHCI_INIT_HAVE_SELFID_BUFFER; - - if ((unsigned long)ohci->selfid_buf_cpu & 0x1fff) - PRINT(KERN_INFO, "SelfID buffer %p is not aligned on " - "8Kb boundary... may cause problems on some CXD3222 chip", - ohci->selfid_buf_cpu); - - /* No self-id errors at startup */ - ohci->self_id_errors = 0; - - ohci->init_state = OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE; - /* AR DMA request context allocation */ - if (alloc_dma_rcv_ctx(ohci, &ohci->ar_req_context, - DMA_CTX_ASYNC_REQ, 0, AR_REQ_NUM_DESC, - AR_REQ_BUF_SIZE, AR_REQ_SPLIT_BUF_SIZE, - OHCI1394_AsReqRcvContextBase) < 0) { - PRINT_G(KERN_ERR, "Failed to allocate %s", "AR Req context"); - goto err; - } - /* AR DMA response context allocation */ - if (alloc_dma_rcv_ctx(ohci, &ohci->ar_resp_context, - DMA_CTX_ASYNC_RESP, 0, AR_RESP_NUM_DESC, - AR_RESP_BUF_SIZE, AR_RESP_SPLIT_BUF_SIZE, - OHCI1394_AsRspRcvContextBase) < 0) { - PRINT_G(KERN_ERR, "Failed to allocate %s", "AR Resp context"); - goto err; - } - /* AT DMA request context */ - if (alloc_dma_trm_ctx(ohci, &ohci->at_req_context, - DMA_CTX_ASYNC_REQ, 0, AT_REQ_NUM_DESC, - OHCI1394_AsReqTrContextBase) < 0) { - PRINT_G(KERN_ERR, "Failed to allocate %s", "AT Req context"); - goto err; - } - /* AT DMA response context */ - if (alloc_dma_trm_ctx(ohci, &ohci->at_resp_context, - DMA_CTX_ASYNC_RESP, 1, AT_RESP_NUM_DESC, - OHCI1394_AsRspTrContextBase) < 0) { - PRINT_G(KERN_ERR, "Failed to allocate %s", "AT Resp context"); - goto err; - } - /* Start off with a soft reset, to clear everything to a sane - * state. */ - ohci_soft_reset(ohci); - - /* Now enable LPS, which we need in order to start accessing - * most of the registers. In fact, on some cards (ALI M5251), - * accessing registers in the SClk domain without LPS enabled - * will lock up the machine. */ - reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS); - - /* Disable and clear interrupts */ - reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); - reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); - - /* Flush MMIO writes and wait to make sure we have full link enabled. */ - reg_read(ohci, OHCI1394_Version); - msleep(50); - - /* Determine the number of available IR and IT contexts. */ - ohci->nb_iso_rcv_ctx = - get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet); - ohci->nb_iso_xmit_ctx = - get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet); - - /* Set the usage bits for non-existent contexts so they can't - * be allocated */ - ohci->ir_ctx_usage = ~0 << ohci->nb_iso_rcv_ctx; - ohci->it_ctx_usage = ~0 << ohci->nb_iso_xmit_ctx; - - INIT_LIST_HEAD(&ohci->iso_tasklet_list); - spin_lock_init(&ohci->iso_tasklet_list_lock); - ohci->ISO_channel_usage = 0; - spin_lock_init(&ohci->IR_channel_lock); - - spin_lock_init(&ohci->event_lock); - - /* - * interrupts are disabled, all right, but... due to IRQF_SHARED we - * might get called anyway. We'll see no event, of course, but - * we need to get to that "no event", so enough should be initialized - * by that point. - */ - err = request_irq(dev->irq, ohci_irq_handler, IRQF_SHARED, - OHCI1394_DRIVER_NAME, ohci); - if (err) { - PRINT_G(KERN_ERR, "Failed to allocate interrupt %d", dev->irq); - goto err; - } - ohci->init_state = OHCI_INIT_HAVE_IRQ; - ohci_initialize(ohci); - - /* Set certain csr values */ - host->csr.guid_hi = reg_read(ohci, OHCI1394_GUIDHi); - host->csr.guid_lo = reg_read(ohci, OHCI1394_GUIDLo); - host->csr.cyc_clk_acc = 100; /* how do we determine clk accuracy? */ - host->csr.max_rec = (reg_read(ohci, OHCI1394_BusOptions) >> 12) & 0xf; - host->csr.lnk_spd = reg_read(ohci, OHCI1394_BusOptions) & 0x7; - - if (phys_dma) { - host->low_addr_space = - (u64) reg_read(ohci, OHCI1394_PhyUpperBound) << 16; - if (!host->low_addr_space) - host->low_addr_space = OHCI1394_PHYS_UPPER_BOUND_FIXED; - } - host->middle_addr_space = OHCI1394_MIDDLE_ADDRESS_SPACE; - - /* Tell the highlevel this host is ready */ - if (hpsb_add_host(host)) { - PRINT_G(KERN_ERR, "Failed to register host with highlevel"); - goto err; - } - ohci->init_state = OHCI_INIT_DONE; - - return 0; -err: - ohci1394_pci_remove(dev); - return err; -} - -static void ohci1394_pci_remove(struct pci_dev *dev) -{ - struct ti_ohci *ohci; - struct device *device; - - ohci = pci_get_drvdata(dev); - if (!ohci) - goto out; - - device = get_device(&ohci->host->device); - - switch (ohci->init_state) { - case OHCI_INIT_DONE: - hpsb_remove_host(ohci->host); - - /* Clear out BUS Options */ - reg_write(ohci, OHCI1394_ConfigROMhdr, 0); - reg_write(ohci, OHCI1394_BusOptions, - (reg_read(ohci, OHCI1394_BusOptions) & 0x0000f007) | - 0x00ff0000); - memset(ohci->csr_config_rom_cpu, 0, OHCI_CONFIG_ROM_LEN); - - case OHCI_INIT_HAVE_IRQ: - /* Clear interrupt registers */ - reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); - - /* Disable IRM Contender */ - set_phy_reg(ohci, 4, ~0xc0 & get_phy_reg(ohci, 4)); - - /* Clear link control register */ - reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff); - - /* Let all other nodes know to ignore us */ - ohci_devctl(ohci->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT); - - /* Soft reset before we start - this disables - * interrupts and clears linkEnable and LPS. */ - ohci_soft_reset(ohci); - free_irq(dev->irq, ohci); - - case OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE: - /* The ohci_soft_reset() stops all DMA contexts, so we - * dont need to do this. */ - free_dma_rcv_ctx(&ohci->ar_req_context); - free_dma_rcv_ctx(&ohci->ar_resp_context); - free_dma_trm_ctx(&ohci->at_req_context); - free_dma_trm_ctx(&ohci->at_resp_context); - - case OHCI_INIT_HAVE_SELFID_BUFFER: - pci_free_consistent(dev, OHCI1394_SI_DMA_BUF_SIZE, - ohci->selfid_buf_cpu, - ohci->selfid_buf_bus); - - case OHCI_INIT_HAVE_CONFIG_ROM_BUFFER: - pci_free_consistent(dev, OHCI_CONFIG_ROM_LEN, - ohci->csr_config_rom_cpu, - ohci->csr_config_rom_bus); - - case OHCI_INIT_HAVE_IOMAPPING: - iounmap(ohci->registers); - - case OHCI_INIT_HAVE_MEM_REGION: - release_mem_region(pci_resource_start(dev, 0), - OHCI1394_REGISTER_SIZE); - - case OHCI_INIT_ALLOC_HOST: - pci_set_drvdata(dev, NULL); - } - - if (device) - put_device(device); -out: - ohci1394_pmac_off(dev); -} - -#ifdef CONFIG_PM -static int ohci1394_pci_suspend(struct pci_dev *dev, pm_message_t state) -{ - int err; - struct ti_ohci *ohci = pci_get_drvdata(dev); - - if (!ohci) { - printk(KERN_ERR "%s: tried to suspend nonexisting host\n", - OHCI1394_DRIVER_NAME); - return -ENXIO; - } - DBGMSG("suspend called"); - - /* Clear the async DMA contexts and stop using the controller */ - hpsb_bus_reset(ohci->host); - - /* See ohci1394_pci_remove() for comments on this sequence */ - reg_write(ohci, OHCI1394_ConfigROMhdr, 0); - reg_write(ohci, OHCI1394_BusOptions, - (reg_read(ohci, OHCI1394_BusOptions) & 0x0000f007) | - 0x00ff0000); - reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); - reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); - set_phy_reg(ohci, 4, ~0xc0 & get_phy_reg(ohci, 4)); - reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff); - ohci_devctl(ohci->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT); - ohci_soft_reset(ohci); - - free_irq(dev->irq, ohci); - err = pci_save_state(dev); - if (err) { - PRINT(KERN_ERR, "pci_save_state failed with %d", err); - return err; - } - err = pci_set_power_state(dev, pci_choose_state(dev, state)); - if (err) - DBGMSG("pci_set_power_state failed with %d", err); - ohci1394_pmac_off(dev); - - return 0; -} - -static int ohci1394_pci_resume(struct pci_dev *dev) -{ - int err; - struct ti_ohci *ohci = pci_get_drvdata(dev); - - if (!ohci) { - printk(KERN_ERR "%s: tried to resume nonexisting host\n", - OHCI1394_DRIVER_NAME); - return -ENXIO; - } - DBGMSG("resume called"); - - ohci1394_pmac_on(dev); - pci_set_power_state(dev, PCI_D0); - pci_restore_state(dev); - err = pci_enable_device(dev); - if (err) { - PRINT(KERN_ERR, "pci_enable_device failed with %d", err); - return err; - } - - /* See ohci1394_pci_probe() for comments on this sequence */ - ohci_soft_reset(ohci); - reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS); - reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); - reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); - reg_read(ohci, OHCI1394_Version); - msleep(50); - - err = request_irq(dev->irq, ohci_irq_handler, IRQF_SHARED, - OHCI1394_DRIVER_NAME, ohci); - if (err) { - PRINT_G(KERN_ERR, "Failed to allocate interrupt %d", dev->irq); - return err; - } - - ohci_initialize(ohci); - - hpsb_resume_host(ohci->host); - return 0; -} -#endif /* CONFIG_PM */ - -static struct pci_device_id ohci1394_pci_tbl[] = { - { - .class = PCI_CLASS_SERIAL_FIREWIRE_OHCI, - .class_mask = PCI_ANY_ID, - .vendor = PCI_ANY_ID, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, - { 0, }, -}; - -MODULE_DEVICE_TABLE(pci, ohci1394_pci_tbl); - -static struct pci_driver ohci1394_pci_driver = { - .name = OHCI1394_DRIVER_NAME, - .id_table = ohci1394_pci_tbl, - .probe = ohci1394_pci_probe, - .remove = ohci1394_pci_remove, -#ifdef CONFIG_PM - .resume = ohci1394_pci_resume, - .suspend = ohci1394_pci_suspend, -#endif -}; - -/*********************************** - * OHCI1394 Video Interface * - ***********************************/ - -/* essentially the only purpose of this code is to allow another - module to hook into ohci's interrupt handler */ - -/* returns zero if successful, one if DMA context is locked up */ -int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg) -{ - int i=0; - - /* stop the channel program if it's still running */ - reg_write(ohci, reg, 0x8000); - - /* Wait until it effectively stops */ - while (reg_read(ohci, reg) & 0x400) { - i++; - if (i>5000) { - PRINT(KERN_ERR, - "Runaway loop while stopping context: %s...", msg ? msg : ""); - return 1; - } - - mb(); - udelay(10); - } - if (msg) PRINT(KERN_ERR, "%s: dma prg stopped", msg); - return 0; -} - -void ohci1394_init_iso_tasklet(struct ohci1394_iso_tasklet *tasklet, int type, - void (*func)(unsigned long), unsigned long data) -{ - tasklet_init(&tasklet->tasklet, func, data); - tasklet->type = type; - /* We init the tasklet->link field, so we can list_del() it - * without worrying whether it was added to the list or not. */ - INIT_LIST_HEAD(&tasklet->link); -} - -int ohci1394_register_iso_tasklet(struct ti_ohci *ohci, - struct ohci1394_iso_tasklet *tasklet) -{ - unsigned long flags, *usage; - int n, i, r = -EBUSY; - - if (tasklet->type == OHCI_ISO_TRANSMIT) { - n = ohci->nb_iso_xmit_ctx; - usage = &ohci->it_ctx_usage; - } - else { - n = ohci->nb_iso_rcv_ctx; - usage = &ohci->ir_ctx_usage; - - /* only one receive context can be multichannel (OHCI sec 10.4.1) */ - if (tasklet->type == OHCI_ISO_MULTICHANNEL_RECEIVE) { - if (test_and_set_bit(0, &ohci->ir_multichannel_used)) { - return r; - } - } - } - - spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags); - - for (i = 0; i < n; i++) - if (!test_and_set_bit(i, usage)) { - tasklet->context = i; - list_add_tail(&tasklet->link, &ohci->iso_tasklet_list); - r = 0; - break; - } - - spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags); - - return r; -} - -void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci, - struct ohci1394_iso_tasklet *tasklet) -{ - unsigned long flags; - - tasklet_kill(&tasklet->tasklet); - - spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags); - - if (tasklet->type == OHCI_ISO_TRANSMIT) - clear_bit(tasklet->context, &ohci->it_ctx_usage); - else { - clear_bit(tasklet->context, &ohci->ir_ctx_usage); - - if (tasklet->type == OHCI_ISO_MULTICHANNEL_RECEIVE) { - clear_bit(0, &ohci->ir_multichannel_used); - } - } - - list_del(&tasklet->link); - - spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags); -} - -EXPORT_SYMBOL(ohci1394_stop_context); -EXPORT_SYMBOL(ohci1394_init_iso_tasklet); -EXPORT_SYMBOL(ohci1394_register_iso_tasklet); -EXPORT_SYMBOL(ohci1394_unregister_iso_tasklet); - -/*********************************** - * General module initialization * - ***********************************/ - -MODULE_AUTHOR("Sebastien Rougeaux "); -MODULE_DESCRIPTION("Driver for PCI OHCI IEEE-1394 controllers"); -MODULE_LICENSE("GPL"); - -static void __exit ohci1394_cleanup (void) -{ - pci_unregister_driver(&ohci1394_pci_driver); -} - -static int __init ohci1394_init(void) -{ - return pci_register_driver(&ohci1394_pci_driver); -} - -module_init(ohci1394_init); -module_exit(ohci1394_cleanup); diff --git a/drivers/ieee1394/ohci1394.h b/drivers/ieee1394/ohci1394.h deleted file mode 100644 index 7fb8ab9780ae..000000000000 --- a/drivers/ieee1394/ohci1394.h +++ /dev/null @@ -1,453 +0,0 @@ -/* - * ohci1394.h - driver for OHCI 1394 boards - * Copyright (C)1999,2000 Sebastien Rougeaux - * Gord Peters - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _OHCI1394_H -#define _OHCI1394_H - -#include "ieee1394_types.h" -#include - -#define OHCI1394_DRIVER_NAME "ohci1394" - -#define OHCI1394_MAX_AT_REQ_RETRIES 0xf -#define OHCI1394_MAX_AT_RESP_RETRIES 0x2 -#define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8 -#define OHCI1394_MAX_SELF_ID_ERRORS 16 - -#define AR_REQ_NUM_DESC 4 /* number of AR req descriptors */ -#define AR_REQ_BUF_SIZE PAGE_SIZE /* size of AR req buffers */ -#define AR_REQ_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */ - -#define AR_RESP_NUM_DESC 4 /* number of AR resp descriptors */ -#define AR_RESP_BUF_SIZE PAGE_SIZE /* size of AR resp buffers */ -#define AR_RESP_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */ - -#define IR_NUM_DESC 16 /* number of IR descriptors */ -#define IR_BUF_SIZE PAGE_SIZE /* 4096 bytes/buffer */ -#define IR_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */ - -#define IT_NUM_DESC 16 /* number of IT descriptors */ - -#define AT_REQ_NUM_DESC 32 /* number of AT req descriptors */ -#define AT_RESP_NUM_DESC 32 /* number of AT resp descriptors */ - -#define OHCI_LOOP_COUNT 100 /* Number of loops for reg read waits */ - -#define OHCI_CONFIG_ROM_LEN 1024 /* Length of the mapped configrom space */ - -#define OHCI1394_SI_DMA_BUF_SIZE 8192 /* length of the selfid buffer */ - -/* PCI configuration space addresses */ -#define OHCI1394_PCI_HCI_Control 0x40 - -struct dma_cmd { - u32 control; - u32 address; - u32 branchAddress; - u32 status; -}; - -/* - * FIXME: - * It is important that a single at_dma_prg does not cross a page boundary - * The proper way to do it would be to do the check dynamically as the - * programs are inserted into the AT fifo. - */ -struct at_dma_prg { - struct dma_cmd begin; - quadlet_t data[4]; - struct dma_cmd end; - quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */ -}; - -/* identify whether a DMA context is asynchronous or isochronous */ -enum context_type { DMA_CTX_ASYNC_REQ, DMA_CTX_ASYNC_RESP, DMA_CTX_ISO }; - -/* DMA receive context */ -struct dma_rcv_ctx { - struct ti_ohci *ohci; - enum context_type type; - int ctx; - unsigned int num_desc; - - unsigned int buf_size; - unsigned int split_buf_size; - - /* dma block descriptors */ - struct dma_cmd **prg_cpu; - dma_addr_t *prg_bus; - struct pci_pool *prg_pool; - - /* dma buffers */ - quadlet_t **buf_cpu; - dma_addr_t *buf_bus; - - unsigned int buf_ind; - unsigned int buf_offset; - quadlet_t *spb; - spinlock_t lock; - struct tasklet_struct task; - int ctrlClear; - int ctrlSet; - int cmdPtr; - int ctxtMatch; -}; - -/* DMA transmit context */ -struct dma_trm_ctx { - struct ti_ohci *ohci; - enum context_type type; - int ctx; - unsigned int num_desc; - - /* dma block descriptors */ - struct at_dma_prg **prg_cpu; - dma_addr_t *prg_bus; - struct pci_pool *prg_pool; - - unsigned int prg_ind; - unsigned int sent_ind; - int free_prgs; - quadlet_t *branchAddrPtr; - - /* list of packets inserted in the AT FIFO */ - struct list_head fifo_list; - - /* list of pending packets to be inserted in the AT FIFO */ - struct list_head pending_list; - - spinlock_t lock; - struct tasklet_struct task; - int ctrlClear; - int ctrlSet; - int cmdPtr; -}; - -struct ohci1394_iso_tasklet { - struct tasklet_struct tasklet; - struct list_head link; - int context; - enum { OHCI_ISO_TRANSMIT, OHCI_ISO_RECEIVE, - OHCI_ISO_MULTICHANNEL_RECEIVE } type; -}; - -struct ti_ohci { - struct pci_dev *dev; - - enum { - OHCI_INIT_ALLOC_HOST, - OHCI_INIT_HAVE_MEM_REGION, - OHCI_INIT_HAVE_IOMAPPING, - OHCI_INIT_HAVE_CONFIG_ROM_BUFFER, - OHCI_INIT_HAVE_SELFID_BUFFER, - OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE, - OHCI_INIT_HAVE_IRQ, - OHCI_INIT_DONE, - } init_state; - - /* remapped memory spaces */ - void __iomem *registers; - - /* dma buffer for self-id packets */ - quadlet_t *selfid_buf_cpu; - dma_addr_t selfid_buf_bus; - - /* buffer for csr config rom */ - quadlet_t *csr_config_rom_cpu; - dma_addr_t csr_config_rom_bus; - int csr_config_rom_length; - - unsigned int max_packet_size; - - /* async receive */ - struct dma_rcv_ctx ar_resp_context; - struct dma_rcv_ctx ar_req_context; - - /* async transmit */ - struct dma_trm_ctx at_resp_context; - struct dma_trm_ctx at_req_context; - - /* iso receive */ - int nb_iso_rcv_ctx; - unsigned long ir_ctx_usage; /* use test_and_set_bit() for atomicity */ - unsigned long ir_multichannel_used; /* ditto */ - spinlock_t IR_channel_lock; - - /* iso transmit */ - int nb_iso_xmit_ctx; - unsigned long it_ctx_usage; /* use test_and_set_bit() for atomicity */ - - u64 ISO_channel_usage; - - /* IEEE-1394 part follows */ - struct hpsb_host *host; - - int phyid, isroot; - - spinlock_t phy_reg_lock; - spinlock_t event_lock; - - int self_id_errors; - - /* Tasklets for iso receive and transmit, used by video1394 - * and dv1394 */ - struct list_head iso_tasklet_list; - spinlock_t iso_tasklet_list_lock; - - /* Swap the selfid buffer? */ - unsigned int selfid_swap:1; - /* Some Apple chipset seem to swap incoming headers for us */ - unsigned int no_swap_incoming:1; - - /* Force extra paranoia checking on bus-reset handling */ - unsigned int check_busreset:1; -}; - -static inline int cross_bound(unsigned long addr, unsigned int size) -{ - if (size == 0) - return 0; - - if (size > PAGE_SIZE) - return 1; - - if (addr >> PAGE_SHIFT != (addr + size - 1) >> PAGE_SHIFT) - return 1; - - return 0; -} - -/* - * Register read and write helper functions. - */ -static inline void reg_write(const struct ti_ohci *ohci, int offset, u32 data) -{ - writel(data, ohci->registers + offset); -} - -static inline u32 reg_read(const struct ti_ohci *ohci, int offset) -{ - return readl(ohci->registers + offset); -} - - -/* 2 KiloBytes of register space */ -#define OHCI1394_REGISTER_SIZE 0x800 - -/* Offsets relative to context bases defined below */ - -#define OHCI1394_ContextControlSet 0x000 -#define OHCI1394_ContextControlClear 0x004 -#define OHCI1394_ContextCommandPtr 0x00C - -/* register map */ -#define OHCI1394_Version 0x000 -#define OHCI1394_GUID_ROM 0x004 -#define OHCI1394_ATRetries 0x008 -#define OHCI1394_CSRData 0x00C -#define OHCI1394_CSRCompareData 0x010 -#define OHCI1394_CSRControl 0x014 -#define OHCI1394_ConfigROMhdr 0x018 -#define OHCI1394_BusID 0x01C -#define OHCI1394_BusOptions 0x020 -#define OHCI1394_GUIDHi 0x024 -#define OHCI1394_GUIDLo 0x028 -#define OHCI1394_ConfigROMmap 0x034 -#define OHCI1394_PostedWriteAddressLo 0x038 -#define OHCI1394_PostedWriteAddressHi 0x03C -#define OHCI1394_VendorID 0x040 -#define OHCI1394_HCControlSet 0x050 -#define OHCI1394_HCControlClear 0x054 -#define OHCI1394_HCControl_noByteSwap 0x40000000 -#define OHCI1394_HCControl_programPhyEnable 0x00800000 -#define OHCI1394_HCControl_aPhyEnhanceEnable 0x00400000 -#define OHCI1394_HCControl_LPS 0x00080000 -#define OHCI1394_HCControl_postedWriteEnable 0x00040000 -#define OHCI1394_HCControl_linkEnable 0x00020000 -#define OHCI1394_HCControl_softReset 0x00010000 -#define OHCI1394_SelfIDBuffer 0x064 -#define OHCI1394_SelfIDCount 0x068 -#define OHCI1394_IRMultiChanMaskHiSet 0x070 -#define OHCI1394_IRMultiChanMaskHiClear 0x074 -#define OHCI1394_IRMultiChanMaskLoSet 0x078 -#define OHCI1394_IRMultiChanMaskLoClear 0x07C -#define OHCI1394_IntEventSet 0x080 -#define OHCI1394_IntEventClear 0x084 -#define OHCI1394_IntMaskSet 0x088 -#define OHCI1394_IntMaskClear 0x08C -#define OHCI1394_IsoXmitIntEventSet 0x090 -#define OHCI1394_IsoXmitIntEventClear 0x094 -#define OHCI1394_IsoXmitIntMaskSet 0x098 -#define OHCI1394_IsoXmitIntMaskClear 0x09C -#define OHCI1394_IsoRecvIntEventSet 0x0A0 -#define OHCI1394_IsoRecvIntEventClear 0x0A4 -#define OHCI1394_IsoRecvIntMaskSet 0x0A8 -#define OHCI1394_IsoRecvIntMaskClear 0x0AC -#define OHCI1394_InitialBandwidthAvailable 0x0B0 -#define OHCI1394_InitialChannelsAvailableHi 0x0B4 -#define OHCI1394_InitialChannelsAvailableLo 0x0B8 -#define OHCI1394_FairnessControl 0x0DC -#define OHCI1394_LinkControlSet 0x0E0 -#define OHCI1394_LinkControlClear 0x0E4 -#define OHCI1394_LinkControl_RcvSelfID 0x00000200 -#define OHCI1394_LinkControl_RcvPhyPkt 0x00000400 -#define OHCI1394_LinkControl_CycleTimerEnable 0x00100000 -#define OHCI1394_LinkControl_CycleMaster 0x00200000 -#define OHCI1394_LinkControl_CycleSource 0x00400000 -#define OHCI1394_NodeID 0x0E8 -#define OHCI1394_PhyControl 0x0EC -#define OHCI1394_IsochronousCycleTimer 0x0F0 -#define OHCI1394_AsReqFilterHiSet 0x100 -#define OHCI1394_AsReqFilterHiClear 0x104 -#define OHCI1394_AsReqFilterLoSet 0x108 -#define OHCI1394_AsReqFilterLoClear 0x10C -#define OHCI1394_PhyReqFilterHiSet 0x110 -#define OHCI1394_PhyReqFilterHiClear 0x114 -#define OHCI1394_PhyReqFilterLoSet 0x118 -#define OHCI1394_PhyReqFilterLoClear 0x11C -#define OHCI1394_PhyUpperBound 0x120 - -#define OHCI1394_AsReqTrContextBase 0x180 -#define OHCI1394_AsReqTrContextControlSet 0x180 -#define OHCI1394_AsReqTrContextControlClear 0x184 -#define OHCI1394_AsReqTrCommandPtr 0x18C - -#define OHCI1394_AsRspTrContextBase 0x1A0 -#define OHCI1394_AsRspTrContextControlSet 0x1A0 -#define OHCI1394_AsRspTrContextControlClear 0x1A4 -#define OHCI1394_AsRspTrCommandPtr 0x1AC - -#define OHCI1394_AsReqRcvContextBase 0x1C0 -#define OHCI1394_AsReqRcvContextControlSet 0x1C0 -#define OHCI1394_AsReqRcvContextControlClear 0x1C4 -#define OHCI1394_AsReqRcvCommandPtr 0x1CC - -#define OHCI1394_AsRspRcvContextBase 0x1E0 -#define OHCI1394_AsRspRcvContextControlSet 0x1E0 -#define OHCI1394_AsRspRcvContextControlClear 0x1E4 -#define OHCI1394_AsRspRcvCommandPtr 0x1EC - -/* Isochronous transmit registers */ -/* Add (16 * n) for context n */ -#define OHCI1394_IsoXmitContextBase 0x200 -#define OHCI1394_IsoXmitContextControlSet 0x200 -#define OHCI1394_IsoXmitContextControlClear 0x204 -#define OHCI1394_IsoXmitCommandPtr 0x20C - -/* Isochronous receive registers */ -/* Add (32 * n) for context n */ -#define OHCI1394_IsoRcvContextBase 0x400 -#define OHCI1394_IsoRcvContextControlSet 0x400 -#define OHCI1394_IsoRcvContextControlClear 0x404 -#define OHCI1394_IsoRcvCommandPtr 0x40C -#define OHCI1394_IsoRcvContextMatch 0x410 - -/* Interrupts Mask/Events */ - -#define OHCI1394_reqTxComplete 0x00000001 -#define OHCI1394_respTxComplete 0x00000002 -#define OHCI1394_ARRQ 0x00000004 -#define OHCI1394_ARRS 0x00000008 -#define OHCI1394_RQPkt 0x00000010 -#define OHCI1394_RSPkt 0x00000020 -#define OHCI1394_isochTx 0x00000040 -#define OHCI1394_isochRx 0x00000080 -#define OHCI1394_postedWriteErr 0x00000100 -#define OHCI1394_lockRespErr 0x00000200 -#define OHCI1394_selfIDComplete 0x00010000 -#define OHCI1394_busReset 0x00020000 -#define OHCI1394_phy 0x00080000 -#define OHCI1394_cycleSynch 0x00100000 -#define OHCI1394_cycle64Seconds 0x00200000 -#define OHCI1394_cycleLost 0x00400000 -#define OHCI1394_cycleInconsistent 0x00800000 -#define OHCI1394_unrecoverableError 0x01000000 -#define OHCI1394_cycleTooLong 0x02000000 -#define OHCI1394_phyRegRcvd 0x04000000 -#define OHCI1394_masterIntEnable 0x80000000 - -/* DMA Control flags */ -#define DMA_CTL_OUTPUT_MORE 0x00000000 -#define DMA_CTL_OUTPUT_LAST 0x10000000 -#define DMA_CTL_INPUT_MORE 0x20000000 -#define DMA_CTL_INPUT_LAST 0x30000000 -#define DMA_CTL_UPDATE 0x08000000 -#define DMA_CTL_IMMEDIATE 0x02000000 -#define DMA_CTL_IRQ 0x00300000 -#define DMA_CTL_BRANCH 0x000c0000 -#define DMA_CTL_WAIT 0x00030000 - -/* OHCI evt_* error types, table 3-2 of the OHCI 1.1 spec. */ -#define EVT_NO_STATUS 0x0 /* No event status */ -#define EVT_RESERVED_A 0x1 /* Reserved, not used !!! */ -#define EVT_LONG_PACKET 0x2 /* The revc data was longer than the buf */ -#define EVT_MISSING_ACK 0x3 /* A subaction gap was detected before an ack - arrived, or recv'd ack had a parity error */ -#define EVT_UNDERRUN 0x4 /* Underrun on corresponding FIFO, packet - truncated */ -#define EVT_OVERRUN 0x5 /* A recv FIFO overflowed on reception of ISO - packet */ -#define EVT_DESCRIPTOR_READ 0x6 /* An unrecoverable error occurred while host was - reading a descriptor block */ -#define EVT_DATA_READ 0x7 /* An error occurred while host controller was - attempting to read from host memory in the data - stage of descriptor processing */ -#define EVT_DATA_WRITE 0x8 /* An error occurred while host controller was - attempting to write either during the data stage - of descriptor processing, or when processing a single - 16-bit host memory write */ -#define EVT_BUS_RESET 0x9 /* Identifies a PHY packet in the recv buffer as - being a synthesized bus reset packet */ -#define EVT_TIMEOUT 0xa /* Indicates that the asynchronous transmit response - packet expired and was not transmitted, or that an - IT DMA context experienced a skip processing overflow */ -#define EVT_TCODE_ERR 0xb /* A bad tCode is associated with this packet. - The packet was flushed */ -#define EVT_RESERVED_B 0xc /* Reserved, not used !!! */ -#define EVT_RESERVED_C 0xd /* Reserved, not used !!! */ -#define EVT_UNKNOWN 0xe /* An error condition has occurred that cannot be - represented by any other event codes defined herein. */ -#define EVT_FLUSHED 0xf /* Send by the link side of output FIFO when asynchronous - packets are being flushed due to a bus reset. */ - -#define OHCI1394_TCODE_PHY 0xE - -/* Node offset map (phys DMA area, posted write area). - * The value of OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED may be modified but must - * be lower than OHCI1394_MIDDLE_ADDRESS_SPACE. - * OHCI1394_PHYS_UPPER_BOUND_FIXED and OHCI1394_MIDDLE_ADDRESS_SPACE are - * constants given by the OHCI spec. - */ -#define OHCI1394_PHYS_UPPER_BOUND_FIXED 0x000100000000ULL /* 4 GB */ -#define OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED 0x010000000000ULL /* 1 TB */ -#define OHCI1394_MIDDLE_ADDRESS_SPACE 0xffff00000000ULL - -void ohci1394_init_iso_tasklet(struct ohci1394_iso_tasklet *tasklet, - int type, - void (*func)(unsigned long), - unsigned long data); -int ohci1394_register_iso_tasklet(struct ti_ohci *ohci, - struct ohci1394_iso_tasklet *tasklet); -void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci, - struct ohci1394_iso_tasklet *tasklet); -int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg); -struct ti_ohci *ohci1394_get_struct(int card_num); - -#endif diff --git a/drivers/ieee1394/pcilynx.c b/drivers/ieee1394/pcilynx.c deleted file mode 100644 index bf47fee79808..000000000000 --- a/drivers/ieee1394/pcilynx.c +++ /dev/null @@ -1,1554 +0,0 @@ -/* - * pcilynx.c - Texas Instruments PCILynx driver - * Copyright (C) 1999,2000 Andreas Bombe , - * Stephan Linz - * Manfred Weihs - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - * Contributions: - * - * Manfred Weihs - * reading bus info block (containing GUID) from serial - * eeprom via i2c and storing it in config ROM - * Reworked code for initiating bus resets - * (long, short, with or without hold-off) - * Enhancements in async and iso send code - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "csr1212.h" -#include "ieee1394.h" -#include "ieee1394_types.h" -#include "hosts.h" -#include "ieee1394_core.h" -#include "highlevel.h" -#include "pcilynx.h" - -#include -#include - -/* print general (card independent) information */ -#define PRINT_G(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args) -/* print card specific information */ -#define PRINT(level, card, fmt, args...) printk(level "pcilynx%d: " fmt "\n" , card , ## args) - -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG -#define PRINT_GD(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args) -#define PRINTD(level, card, fmt, args...) printk(level "pcilynx%d: " fmt "\n" , card , ## args) -#else -#define PRINT_GD(level, fmt, args...) do {} while (0) -#define PRINTD(level, card, fmt, args...) do {} while (0) -#endif - - -/* Module Parameters */ -static int skip_eeprom; -module_param(skip_eeprom, int, 0444); -MODULE_PARM_DESC(skip_eeprom, "Use generic bus info block instead of serial eeprom (default = 0)."); - - -static struct hpsb_host_driver lynx_driver; -static unsigned int card_id; - - - -/* - * I2C stuff - */ - -/* the i2c stuff was inspired by i2c-philips-par.c */ - -static void bit_setscl(void *data, int state) -{ - if (state) { - ((struct ti_lynx *) data)->i2c_driven_state |= 0x00000040; - } else { - ((struct ti_lynx *) data)->i2c_driven_state &= ~0x00000040; - } - reg_write((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL, ((struct ti_lynx *) data)->i2c_driven_state); -} - -static void bit_setsda(void *data, int state) -{ - if (state) { - ((struct ti_lynx *) data)->i2c_driven_state |= 0x00000010; - } else { - ((struct ti_lynx *) data)->i2c_driven_state &= ~0x00000010; - } - reg_write((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL, ((struct ti_lynx *) data)->i2c_driven_state); -} - -static int bit_getscl(void *data) -{ - return reg_read((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL) & 0x00000040; -} - -static int bit_getsda(void *data) -{ - return reg_read((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL) & 0x00000010; -} - -static struct i2c_algo_bit_data bit_data = { - .setsda = bit_setsda, - .setscl = bit_setscl, - .getsda = bit_getsda, - .getscl = bit_getscl, - .udelay = 5, - .timeout = 100, -}; - - -/* - * PCL handling functions. - */ - -static pcl_t alloc_pcl(struct ti_lynx *lynx) -{ - u8 m; - int i, j; - - spin_lock(&lynx->lock); - /* FIXME - use ffz() to make this readable */ - for (i = 0; i < (LOCALRAM_SIZE / 1024); i++) { - m = lynx->pcl_bmap[i]; - for (j = 0; j < 8; j++) { - if (m & 1<pcl_bmap[i] = m; - spin_unlock(&lynx->lock); - return 8 * i + j; - } - } - spin_unlock(&lynx->lock); - - return -1; -} - - -#if 0 -static void free_pcl(struct ti_lynx *lynx, pcl_t pclid) -{ - int off, bit; - - off = pclid / 8; - bit = pclid % 8; - - if (pclid < 0) { - return; - } - - spin_lock(&lynx->lock); - if (lynx->pcl_bmap[off] & 1<pcl_bmap[off] &= ~(1<id, - "attempted to free unallocated PCL %d", pclid); - } - spin_unlock(&lynx->lock); -} - -/* functions useful for debugging */ -static void pretty_print_pcl(const struct ti_pcl *pcl) -{ - int i; - - printk("PCL next %08x, userdata %08x, status %08x, remtrans %08x, nextbuf %08x\n", - pcl->next, pcl->user_data, pcl->pcl_status, - pcl->remaining_transfer_count, pcl->next_data_buffer); - - printk("PCL"); - for (i=0; i<13; i++) { - printk(" c%x:%08x d%x:%08x", - i, pcl->buffer[i].control, i, pcl->buffer[i].pointer); - if (!(i & 0x3) && (i != 12)) printk("\nPCL"); - } - printk("\n"); -} - -static void print_pcl(const struct ti_lynx *lynx, pcl_t pclid) -{ - struct ti_pcl pcl; - - get_pcl(lynx, pclid, &pcl); - pretty_print_pcl(&pcl); -} -#endif - - - -/*********************************** - * IEEE-1394 functionality section * - ***********************************/ - - -static int get_phy_reg(struct ti_lynx *lynx, int addr) -{ - int retval; - int i = 0; - - unsigned long flags; - - if (addr > 15) { - PRINT(KERN_ERR, lynx->id, - "%s: PHY register address %d out of range", - __func__, addr); - return -1; - } - - spin_lock_irqsave(&lynx->phy_reg_lock, flags); - - reg_write(lynx, LINK_PHY, LINK_PHY_READ | LINK_PHY_ADDR(addr)); - do { - retval = reg_read(lynx, LINK_PHY); - - if (i > 10000) { - PRINT(KERN_ERR, lynx->id, "%s: runaway loop, aborting", - __func__); - retval = -1; - break; - } - i++; - } while ((retval & 0xf00) != LINK_PHY_RADDR(addr)); - - reg_write(lynx, LINK_INT_STATUS, LINK_INT_PHY_REG_RCVD); - spin_unlock_irqrestore(&lynx->phy_reg_lock, flags); - - if (retval != -1) { - return retval & 0xff; - } else { - return -1; - } -} - -static int set_phy_reg(struct ti_lynx *lynx, int addr, int val) -{ - unsigned long flags; - - if (addr > 15) { - PRINT(KERN_ERR, lynx->id, - "%s: PHY register address %d out of range", __func__, addr); - return -1; - } - - if (val > 0xff) { - PRINT(KERN_ERR, lynx->id, - "%s: PHY register value %d out of range", __func__, val); - return -1; - } - - spin_lock_irqsave(&lynx->phy_reg_lock, flags); - - reg_write(lynx, LINK_PHY, LINK_PHY_WRITE | LINK_PHY_ADDR(addr) - | LINK_PHY_WDATA(val)); - - spin_unlock_irqrestore(&lynx->phy_reg_lock, flags); - - return 0; -} - -static int sel_phy_reg_page(struct ti_lynx *lynx, int page) -{ - int reg; - - if (page > 7) { - PRINT(KERN_ERR, lynx->id, - "%s: PHY page %d out of range", __func__, page); - return -1; - } - - reg = get_phy_reg(lynx, 7); - if (reg != -1) { - reg &= 0x1f; - reg |= (page << 5); - set_phy_reg(lynx, 7, reg); - return 0; - } else { - return -1; - } -} - -#if 0 /* not needed at this time */ -static int sel_phy_reg_port(struct ti_lynx *lynx, int port) -{ - int reg; - - if (port > 15) { - PRINT(KERN_ERR, lynx->id, - "%s: PHY port %d out of range", __func__, port); - return -1; - } - - reg = get_phy_reg(lynx, 7); - if (reg != -1) { - reg &= 0xf0; - reg |= port; - set_phy_reg(lynx, 7, reg); - return 0; - } else { - return -1; - } -} -#endif - -static u32 get_phy_vendorid(struct ti_lynx *lynx) -{ - u32 pvid = 0; - sel_phy_reg_page(lynx, 1); - pvid |= (get_phy_reg(lynx, 10) << 16); - pvid |= (get_phy_reg(lynx, 11) << 8); - pvid |= get_phy_reg(lynx, 12); - PRINT(KERN_INFO, lynx->id, "PHY vendor id 0x%06x", pvid); - return pvid; -} - -static u32 get_phy_productid(struct ti_lynx *lynx) -{ - u32 id = 0; - sel_phy_reg_page(lynx, 1); - id |= (get_phy_reg(lynx, 13) << 16); - id |= (get_phy_reg(lynx, 14) << 8); - id |= get_phy_reg(lynx, 15); - PRINT(KERN_INFO, lynx->id, "PHY product id 0x%06x", id); - return id; -} - -static quadlet_t generate_own_selfid(struct ti_lynx *lynx, - struct hpsb_host *host) -{ - quadlet_t lsid; - char phyreg[7]; - int i; - - phyreg[0] = lynx->phy_reg0; - for (i = 1; i < 7; i++) { - phyreg[i] = get_phy_reg(lynx, i); - } - - /* FIXME? We assume a TSB21LV03A phy here. This code doesn't support - more than 3 ports on the PHY anyway. */ - - lsid = 0x80400000 | ((phyreg[0] & 0xfc) << 22); - lsid |= (phyreg[1] & 0x3f) << 16; /* gap count */ - lsid |= (phyreg[2] & 0xc0) << 8; /* max speed */ - if (!hpsb_disable_irm) - lsid |= (phyreg[6] & 0x01) << 11; /* contender (phy dependent) */ - /* lsid |= 1 << 11; *//* set contender (hack) */ - lsid |= (phyreg[6] & 0x10) >> 3; /* initiated reset */ - - for (i = 0; i < (phyreg[2] & 0xf); i++) { /* ports */ - if (phyreg[3 + i] & 0x4) { - lsid |= (((phyreg[3 + i] & 0x8) | 0x10) >> 3) - << (6 - i*2); - } else { - lsid |= 1 << (6 - i*2); - } - } - - cpu_to_be32s(&lsid); - PRINT(KERN_DEBUG, lynx->id, "generated own selfid 0x%x", lsid); - return lsid; -} - -static void handle_selfid(struct ti_lynx *lynx, struct hpsb_host *host) -{ - quadlet_t *q = lynx->rcv_page; - int phyid, isroot, size; - quadlet_t lsid = 0; - int i; - - if (lynx->phy_reg0 == -1 || lynx->selfid_size == -1) return; - - size = lynx->selfid_size; - phyid = lynx->phy_reg0; - - i = (size > 16 ? 16 : size) / 4 - 1; - while (i >= 0) { - cpu_to_be32s(&q[i]); - i--; - } - - if (!lynx->phyic.reg_1394a) { - lsid = generate_own_selfid(lynx, host); - } - - isroot = (phyid & 2) != 0; - phyid >>= 2; - PRINT(KERN_INFO, lynx->id, "SelfID process finished (phyid %d, %s)", - phyid, (isroot ? "root" : "not root")); - reg_write(lynx, LINK_ID, (0xffc0 | phyid) << 16); - - if (!lynx->phyic.reg_1394a && !size) { - hpsb_selfid_received(host, lsid); - } - - while (size > 0) { - struct selfid *sid = (struct selfid *)q; - - if (!lynx->phyic.reg_1394a && !sid->extended - && (sid->phy_id == (phyid + 1))) { - hpsb_selfid_received(host, lsid); - } - - if (q[0] == ~q[1]) { - PRINT(KERN_DEBUG, lynx->id, "SelfID packet 0x%x rcvd", - q[0]); - hpsb_selfid_received(host, q[0]); - } else { - PRINT(KERN_INFO, lynx->id, - "inconsistent selfid 0x%x/0x%x", q[0], q[1]); - } - q += 2; - size -= 8; - } - - if (!lynx->phyic.reg_1394a && isroot && phyid != 0) { - hpsb_selfid_received(host, lsid); - } - - hpsb_selfid_complete(host, phyid, isroot); - - if (host->in_bus_reset) return; /* in bus reset again */ - - if (isroot) reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_CYCMASTER); //FIXME: I do not think, we need this here - reg_set_bits(lynx, LINK_CONTROL, - LINK_CONTROL_RCV_CMP_VALID | LINK_CONTROL_TX_ASYNC_EN - | LINK_CONTROL_RX_ASYNC_EN | LINK_CONTROL_CYCTIMEREN); -} - - - -/* This must be called with the respective queue_lock held. */ -static void send_next(struct ti_lynx *lynx, int what) -{ - struct ti_pcl pcl; - struct lynx_send_data *d; - struct hpsb_packet *packet; - -#if 0 /* has been removed from ieee1394 core */ - d = (what == hpsb_iso ? &lynx->iso_send : &lynx->async); -#else - d = &lynx->async; -#endif - if (!list_empty(&d->pcl_queue)) { - PRINT(KERN_ERR, lynx->id, "trying to queue a new packet in nonempty fifo"); - BUG(); - } - - packet = driver_packet(d->queue.next); - list_move_tail(&packet->driver_list, &d->pcl_queue); - - d->header_dma = pci_map_single(lynx->dev, packet->header, - packet->header_size, PCI_DMA_TODEVICE); - if (packet->data_size) { - d->data_dma = pci_map_single(lynx->dev, packet->data, - packet->data_size, - PCI_DMA_TODEVICE); - } else { - d->data_dma = 0; - } - - pcl.next = PCL_NEXT_INVALID; - pcl.async_error_next = PCL_NEXT_INVALID; - pcl.pcl_status = 0; - pcl.buffer[0].control = packet->speed_code << 14 | packet->header_size; -#ifndef __BIG_ENDIAN - pcl.buffer[0].control |= PCL_BIGENDIAN; -#endif - pcl.buffer[0].pointer = d->header_dma; - pcl.buffer[1].control = PCL_LAST_BUFF | packet->data_size; - pcl.buffer[1].pointer = d->data_dma; - - switch (packet->type) { - case hpsb_async: - pcl.buffer[0].control |= PCL_CMD_XMT; - break; -#if 0 /* has been removed from ieee1394 core */ - case hpsb_iso: - pcl.buffer[0].control |= PCL_CMD_XMT | PCL_ISOMODE; - break; -#endif - case hpsb_raw: - pcl.buffer[0].control |= PCL_CMD_UNFXMT; - break; - } - - put_pcl(lynx, d->pcl, &pcl); - run_pcl(lynx, d->pcl_start, d->channel); -} - - -/* called from subsystem core */ -static int lynx_transmit(struct hpsb_host *host, struct hpsb_packet *packet) -{ - struct ti_lynx *lynx = host->hostdata; - struct lynx_send_data *d; - unsigned long flags; - - if (packet->data_size >= 4096) { - PRINT(KERN_ERR, lynx->id, "transmit packet data too big (%Zd)", - packet->data_size); - return -EOVERFLOW; - } - - switch (packet->type) { - case hpsb_async: - case hpsb_raw: - d = &lynx->async; - break; -#if 0 /* has been removed from ieee1394 core */ - case hpsb_iso: - d = &lynx->iso_send; - break; -#endif - default: - PRINT(KERN_ERR, lynx->id, "invalid packet type %d", - packet->type); - return -EINVAL; - } - - if (packet->tcode == TCODE_WRITEQ - || packet->tcode == TCODE_READQ_RESPONSE) { - cpu_to_be32s(&packet->header[3]); - } - - spin_lock_irqsave(&d->queue_lock, flags); - - list_add_tail(&packet->driver_list, &d->queue); - if (list_empty(&d->pcl_queue)) - send_next(lynx, packet->type); - - spin_unlock_irqrestore(&d->queue_lock, flags); - - return 0; -} - - -/* called from subsystem core */ -static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) -{ - struct ti_lynx *lynx = host->hostdata; - int retval = 0; - struct hpsb_packet *packet; - LIST_HEAD(packet_list); - unsigned long flags; - int phy_reg; - - switch (cmd) { - case RESET_BUS: - if (reg_read(lynx, LINK_INT_STATUS) & LINK_INT_PHY_BUSRESET) { - retval = 0; - break; - } - - switch (arg) { - case SHORT_RESET: - if (lynx->phyic.reg_1394a) { - phy_reg = get_phy_reg(lynx, 5); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - phy_reg |= 0x40; - - PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset) on request"); - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - set_phy_reg(lynx, 5, phy_reg); /* set ISBR */ - break; - } else { - PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy"); - /* fall through to long bus reset */ - } - case LONG_RESET: - phy_reg = get_phy_reg(lynx, 1); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - phy_reg |= 0x40; - - PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset) on request"); - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - set_phy_reg(lynx, 1, phy_reg); /* clear RHB, set IBR */ - break; - case SHORT_RESET_NO_FORCE_ROOT: - if (lynx->phyic.reg_1394a) { - phy_reg = get_phy_reg(lynx, 1); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - if (phy_reg & 0x80) { - phy_reg &= ~0x80; - set_phy_reg(lynx, 1, phy_reg); /* clear RHB */ - } - - phy_reg = get_phy_reg(lynx, 5); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - phy_reg |= 0x40; - - PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset, no force_root) on request"); - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - set_phy_reg(lynx, 5, phy_reg); /* set ISBR */ - break; - } else { - PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy"); - /* fall through to long bus reset */ - } - case LONG_RESET_NO_FORCE_ROOT: - phy_reg = get_phy_reg(lynx, 1); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - phy_reg &= ~0x80; - phy_reg |= 0x40; - - PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset, no force_root) on request"); - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - set_phy_reg(lynx, 1, phy_reg); /* clear RHB, set IBR */ - break; - case SHORT_RESET_FORCE_ROOT: - if (lynx->phyic.reg_1394a) { - phy_reg = get_phy_reg(lynx, 1); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - if (!(phy_reg & 0x80)) { - phy_reg |= 0x80; - set_phy_reg(lynx, 1, phy_reg); /* set RHB */ - } - - phy_reg = get_phy_reg(lynx, 5); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - phy_reg |= 0x40; - - PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset, force_root set) on request"); - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - set_phy_reg(lynx, 5, phy_reg); /* set ISBR */ - break; - } else { - PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy"); - /* fall through to long bus reset */ - } - case LONG_RESET_FORCE_ROOT: - phy_reg = get_phy_reg(lynx, 1); - if (phy_reg == -1) { - PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); - retval = -1; - break; - } - phy_reg |= 0xc0; - - PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset, force_root set) on request"); - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - set_phy_reg(lynx, 1, phy_reg); /* set IBR and RHB */ - break; - default: - PRINT(KERN_ERR, lynx->id, "unknown argument for reset_bus command %d", arg); - retval = -1; - } - - break; - - case GET_CYCLE_COUNTER: - retval = reg_read(lynx, CYCLE_TIMER); - break; - - case SET_CYCLE_COUNTER: - reg_write(lynx, CYCLE_TIMER, arg); - break; - - case SET_BUS_ID: - reg_write(lynx, LINK_ID, - (arg << 22) | (reg_read(lynx, LINK_ID) & 0x003f0000)); - break; - - case ACT_CYCLE_MASTER: - if (arg) { - reg_set_bits(lynx, LINK_CONTROL, - LINK_CONTROL_CYCMASTER); - } else { - reg_clear_bits(lynx, LINK_CONTROL, - LINK_CONTROL_CYCMASTER); - } - break; - - case CANCEL_REQUESTS: - spin_lock_irqsave(&lynx->async.queue_lock, flags); - - reg_write(lynx, DMA_CHAN_CTRL(CHANNEL_ASYNC_SEND), 0); - list_splice_init(&lynx->async.queue, &packet_list); - - if (list_empty(&lynx->async.pcl_queue)) { - spin_unlock_irqrestore(&lynx->async.queue_lock, flags); - PRINTD(KERN_DEBUG, lynx->id, "no async packet in PCL to cancel"); - } else { - struct ti_pcl pcl; - u32 ack; - - PRINT(KERN_INFO, lynx->id, "cancelling async packet, that was already in PCL"); - - get_pcl(lynx, lynx->async.pcl, &pcl); - - packet = driver_packet(lynx->async.pcl_queue.next); - list_del_init(&packet->driver_list); - - pci_unmap_single(lynx->dev, lynx->async.header_dma, - packet->header_size, PCI_DMA_TODEVICE); - if (packet->data_size) { - pci_unmap_single(lynx->dev, lynx->async.data_dma, - packet->data_size, PCI_DMA_TODEVICE); - } - - spin_unlock_irqrestore(&lynx->async.queue_lock, flags); - - if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) { - if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) { - ack = (pcl.pcl_status >> 15) & 0xf; - PRINTD(KERN_INFO, lynx->id, "special ack %d", ack); - ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR); - } else { - ack = (pcl.pcl_status >> 15) & 0xf; - } - } else { - PRINT(KERN_INFO, lynx->id, "async packet was not completed"); - ack = ACKX_ABORTED; - } - hpsb_packet_sent(host, packet, ack); - } - - while (!list_empty(&packet_list)) { - packet = driver_packet(packet_list.next); - list_del_init(&packet->driver_list); - hpsb_packet_sent(host, packet, ACKX_ABORTED); - } - - break; -#if 0 /* has been removed from ieee1394 core */ - case ISO_LISTEN_CHANNEL: - spin_lock_irqsave(&lynx->iso_rcv.lock, flags); - - if (lynx->iso_rcv.chan_count++ == 0) { - reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), - DMA_WORD1_CMP_ENABLE_MASTER); - } - - spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags); - break; - - case ISO_UNLISTEN_CHANNEL: - spin_lock_irqsave(&lynx->iso_rcv.lock, flags); - - if (--lynx->iso_rcv.chan_count == 0) { - reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), - 0); - } - - spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags); - break; -#endif - default: - PRINT(KERN_ERR, lynx->id, "unknown devctl command %d", cmd); - retval = -1; - } - - return retval; -} - - -/*************************************** - * IEEE-1394 functionality section END * - ***************************************/ - - -/******************************************************** - * Global stuff (interrupt handler, init/shutdown code) * - ********************************************************/ - - -static irqreturn_t lynx_irq_handler(int irq, void *dev_id) -{ - struct ti_lynx *lynx = (struct ti_lynx *)dev_id; - struct hpsb_host *host = lynx->host; - u32 intmask; - u32 linkint; - - linkint = reg_read(lynx, LINK_INT_STATUS); - intmask = reg_read(lynx, PCI_INT_STATUS); - - if (!(intmask & PCI_INT_INT_PEND)) - return IRQ_NONE; - - PRINTD(KERN_DEBUG, lynx->id, "interrupt: 0x%08x / 0x%08x", intmask, - linkint); - - reg_write(lynx, LINK_INT_STATUS, linkint); - reg_write(lynx, PCI_INT_STATUS, intmask); - - if (intmask & PCI_INT_1394) { - if (linkint & LINK_INT_PHY_TIMEOUT) { - PRINT(KERN_INFO, lynx->id, "PHY timeout occurred"); - } - if (linkint & LINK_INT_PHY_BUSRESET) { - PRINT(KERN_INFO, lynx->id, "bus reset interrupt"); - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - if (!host->in_bus_reset) - hpsb_bus_reset(host); - } - if (linkint & LINK_INT_PHY_REG_RCVD) { - u32 reg; - - spin_lock(&lynx->phy_reg_lock); - reg = reg_read(lynx, LINK_PHY); - spin_unlock(&lynx->phy_reg_lock); - - if (!host->in_bus_reset) { - PRINT(KERN_INFO, lynx->id, - "phy reg received without reset"); - } else if (reg & 0xf00) { - PRINT(KERN_INFO, lynx->id, - "unsolicited phy reg %d received", - (reg >> 8) & 0xf); - } else { - lynx->phy_reg0 = reg & 0xff; - handle_selfid(lynx, host); - } - } - if (linkint & LINK_INT_ISO_STUCK) { - PRINT(KERN_INFO, lynx->id, "isochronous transmitter stuck"); - } - if (linkint & LINK_INT_ASYNC_STUCK) { - PRINT(KERN_INFO, lynx->id, "asynchronous transmitter stuck"); - } - if (linkint & LINK_INT_SENT_REJECT) { - PRINT(KERN_INFO, lynx->id, "sent reject"); - } - if (linkint & LINK_INT_TX_INVALID_TC) { - PRINT(KERN_INFO, lynx->id, "invalid transaction code"); - } - if (linkint & LINK_INT_GRF_OVERFLOW) { - /* flush FIFO if overflow happens during reset */ - if (host->in_bus_reset) - reg_write(lynx, FIFO_CONTROL, - FIFO_CONTROL_GRF_FLUSH); - PRINT(KERN_INFO, lynx->id, "GRF overflow"); - } - if (linkint & LINK_INT_ITF_UNDERFLOW) { - PRINT(KERN_INFO, lynx->id, "ITF underflow"); - } - if (linkint & LINK_INT_ATF_UNDERFLOW) { - PRINT(KERN_INFO, lynx->id, "ATF underflow"); - } - } - - if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_RCV)) { - PRINTD(KERN_DEBUG, lynx->id, "iso receive"); - - spin_lock(&lynx->iso_rcv.lock); - - lynx->iso_rcv.stat[lynx->iso_rcv.next] = - reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ISO_RCV)); - - lynx->iso_rcv.used++; - lynx->iso_rcv.next = (lynx->iso_rcv.next + 1) % NUM_ISORCV_PCL; - - if ((lynx->iso_rcv.next == lynx->iso_rcv.last) - || !lynx->iso_rcv.chan_count) { - PRINTD(KERN_DEBUG, lynx->id, "stopped"); - reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0); - } - - run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, lynx->iso_rcv.next, - CHANNEL_ISO_RCV); - - spin_unlock(&lynx->iso_rcv.lock); - - tasklet_schedule(&lynx->iso_rcv.tq); - } - - if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_SEND)) { - PRINTD(KERN_DEBUG, lynx->id, "async sent"); - spin_lock(&lynx->async.queue_lock); - - if (list_empty(&lynx->async.pcl_queue)) { - spin_unlock(&lynx->async.queue_lock); - PRINT(KERN_WARNING, lynx->id, "async dma halted, but no queued packet (maybe it was cancelled)"); - } else { - struct ti_pcl pcl; - u32 ack; - struct hpsb_packet *packet; - - get_pcl(lynx, lynx->async.pcl, &pcl); - - packet = driver_packet(lynx->async.pcl_queue.next); - list_del_init(&packet->driver_list); - - pci_unmap_single(lynx->dev, lynx->async.header_dma, - packet->header_size, PCI_DMA_TODEVICE); - if (packet->data_size) { - pci_unmap_single(lynx->dev, lynx->async.data_dma, - packet->data_size, PCI_DMA_TODEVICE); - } - - if (!list_empty(&lynx->async.queue)) { - send_next(lynx, hpsb_async); - } - - spin_unlock(&lynx->async.queue_lock); - - if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) { - if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) { - ack = (pcl.pcl_status >> 15) & 0xf; - PRINTD(KERN_INFO, lynx->id, "special ack %d", ack); - ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR); - } else { - ack = (pcl.pcl_status >> 15) & 0xf; - } - } else { - PRINT(KERN_INFO, lynx->id, "async packet was not completed"); - ack = ACKX_SEND_ERROR; - } - hpsb_packet_sent(host, packet, ack); - } - } - - if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_SEND)) { - PRINTD(KERN_DEBUG, lynx->id, "iso sent"); - spin_lock(&lynx->iso_send.queue_lock); - - if (list_empty(&lynx->iso_send.pcl_queue)) { - spin_unlock(&lynx->iso_send.queue_lock); - PRINT(KERN_ERR, lynx->id, "iso send dma halted, but no queued packet"); - } else { - struct ti_pcl pcl; - u32 ack; - struct hpsb_packet *packet; - - get_pcl(lynx, lynx->iso_send.pcl, &pcl); - - packet = driver_packet(lynx->iso_send.pcl_queue.next); - list_del_init(&packet->driver_list); - - pci_unmap_single(lynx->dev, lynx->iso_send.header_dma, - packet->header_size, PCI_DMA_TODEVICE); - if (packet->data_size) { - pci_unmap_single(lynx->dev, lynx->iso_send.data_dma, - packet->data_size, PCI_DMA_TODEVICE); - } -#if 0 /* has been removed from ieee1394 core */ - if (!list_empty(&lynx->iso_send.queue)) { - send_next(lynx, hpsb_iso); - } -#endif - spin_unlock(&lynx->iso_send.queue_lock); - - if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) { - if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) { - ack = (pcl.pcl_status >> 15) & 0xf; - PRINTD(KERN_INFO, lynx->id, "special ack %d", ack); - ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR); - } else { - ack = (pcl.pcl_status >> 15) & 0xf; - } - } else { - PRINT(KERN_INFO, lynx->id, "iso send packet was not completed"); - ack = ACKX_SEND_ERROR; - } - - hpsb_packet_sent(host, packet, ack); //FIXME: maybe we should just use ACK_COMPLETE and ACKX_SEND_ERROR - } - } - - if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_RCV)) { - /* general receive DMA completed */ - int stat = reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ASYNC_RCV)); - - PRINTD(KERN_DEBUG, lynx->id, "received packet size %d", - stat & 0x1fff); - - if (stat & DMA_CHAN_STAT_SELFID) { - lynx->selfid_size = stat & 0x1fff; - handle_selfid(lynx, host); - } else { - quadlet_t *q_data = lynx->rcv_page; - if ((*q_data >> 4 & 0xf) == TCODE_READQ_RESPONSE - || (*q_data >> 4 & 0xf) == TCODE_WRITEQ) { - cpu_to_be32s(q_data + 3); - } - hpsb_packet_received(host, q_data, stat & 0x1fff, 0); - } - - run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV); - } - - return IRQ_HANDLED; -} - - -static void iso_rcv_bh(struct ti_lynx *lynx) -{ - unsigned int idx; - quadlet_t *data; - unsigned long flags; - - spin_lock_irqsave(&lynx->iso_rcv.lock, flags); - - while (lynx->iso_rcv.used) { - idx = lynx->iso_rcv.last; - spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags); - - data = lynx->iso_rcv.page[idx / ISORCV_PER_PAGE] - + (idx % ISORCV_PER_PAGE) * MAX_ISORCV_SIZE; - - if ((*data >> 16) + 4 != (lynx->iso_rcv.stat[idx] & 0x1fff)) { - PRINT(KERN_ERR, lynx->id, - "iso length mismatch 0x%08x/0x%08x", *data, - lynx->iso_rcv.stat[idx]); - } - - if (lynx->iso_rcv.stat[idx] - & (DMA_CHAN_STAT_PCIERR | DMA_CHAN_STAT_PKTERR)) { - PRINT(KERN_INFO, lynx->id, - "iso receive error on %d to 0x%p", idx, data); - } else { - hpsb_packet_received(lynx->host, data, - lynx->iso_rcv.stat[idx] & 0x1fff, - 0); - } - - spin_lock_irqsave(&lynx->iso_rcv.lock, flags); - lynx->iso_rcv.last = (idx + 1) % NUM_ISORCV_PCL; - lynx->iso_rcv.used--; - } - - if (lynx->iso_rcv.chan_count) { - reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), - DMA_WORD1_CMP_ENABLE_MASTER); - } - spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags); -} - - -static void remove_card(struct pci_dev *dev) -{ - struct ti_lynx *lynx; - struct device *lynx_dev; - int i; - - lynx = pci_get_drvdata(dev); - if (!lynx) return; - pci_set_drvdata(dev, NULL); - - lynx_dev = get_device(&lynx->host->device); - - switch (lynx->state) { - case is_host: - reg_write(lynx, PCI_INT_ENABLE, 0); - hpsb_remove_host(lynx->host); - case have_intr: - reg_write(lynx, PCI_INT_ENABLE, 0); - free_irq(lynx->dev->irq, lynx); - - /* Disable IRM Contender and LCtrl */ - if (lynx->phyic.reg_1394a) - set_phy_reg(lynx, 4, ~0xc0 & get_phy_reg(lynx, 4)); - - /* Let all other nodes know to ignore us */ - lynx_devctl(lynx->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT); - - case have_iomappings: - reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); - /* Fix buggy cards with autoboot pin not tied low: */ - reg_write(lynx, DMA0_CHAN_CTRL, 0); - iounmap(lynx->registers); - iounmap(lynx->local_rom); - iounmap(lynx->local_ram); - iounmap(lynx->aux_port); - case have_1394_buffers: - for (i = 0; i < ISORCV_PAGES; i++) { - if (lynx->iso_rcv.page[i]) { - pci_free_consistent(lynx->dev, PAGE_SIZE, - lynx->iso_rcv.page[i], - lynx->iso_rcv.page_dma[i]); - } - } - pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page, - lynx->rcv_page_dma); - case have_aux_buf: - case have_pcl_mem: - pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem, - lynx->pcl_mem_dma); - case clear: - /* do nothing - already freed */ - ; - } - - tasklet_kill(&lynx->iso_rcv.tq); - - if (lynx_dev) - put_device(lynx_dev); -} - - -static int __devinit add_card(struct pci_dev *dev, - const struct pci_device_id *devid_is_unused) -{ -#define FAIL(fmt, args...) do { \ - PRINT_G(KERN_ERR, fmt , ## args); \ - remove_card(dev); \ - return error; \ - } while (0) - - char irq_buf[16]; - struct hpsb_host *host; - struct ti_lynx *lynx; /* shortcut to currently handled device */ - struct ti_pcl pcl; - u32 *pcli; - int i; - int error; - - error = -ENXIO; - - if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) - FAIL("DMA address limits not supported for PCILynx hardware"); - if (pci_enable_device(dev)) - FAIL("failed to enable PCILynx hardware"); - pci_set_master(dev); - - error = -ENOMEM; - - host = hpsb_alloc_host(&lynx_driver, sizeof(struct ti_lynx), &dev->dev); - if (!host) FAIL("failed to allocate control structure memory"); - - lynx = host->hostdata; - lynx->id = card_id++; - lynx->dev = dev; - lynx->state = clear; - lynx->host = host; - host->pdev = dev; - pci_set_drvdata(dev, lynx); - - spin_lock_init(&lynx->lock); - spin_lock_init(&lynx->phy_reg_lock); - - lynx->pcl_mem = pci_alloc_consistent(dev, LOCALRAM_SIZE, - &lynx->pcl_mem_dma); - - if (lynx->pcl_mem != NULL) { - lynx->state = have_pcl_mem; - PRINT(KERN_INFO, lynx->id, - "allocated PCL memory %d Bytes @ 0x%p", LOCALRAM_SIZE, - lynx->pcl_mem); - } else { - FAIL("failed to allocate PCL memory area"); - } - - lynx->rcv_page = pci_alloc_consistent(dev, PAGE_SIZE, - &lynx->rcv_page_dma); - if (lynx->rcv_page == NULL) { - FAIL("failed to allocate receive buffer"); - } - lynx->state = have_1394_buffers; - - for (i = 0; i < ISORCV_PAGES; i++) { - lynx->iso_rcv.page[i] = - pci_alloc_consistent(dev, PAGE_SIZE, - &lynx->iso_rcv.page_dma[i]); - if (lynx->iso_rcv.page[i] == NULL) { - FAIL("failed to allocate iso receive buffers"); - } - } - - lynx->registers = ioremap_nocache(pci_resource_start(dev,0), - PCILYNX_MAX_REGISTER); - lynx->local_ram = ioremap(pci_resource_start(dev,1), PCILYNX_MAX_MEMORY); - lynx->aux_port = ioremap(pci_resource_start(dev,2), PCILYNX_MAX_MEMORY); - lynx->local_rom = ioremap(pci_resource_start(dev,PCI_ROM_RESOURCE), - PCILYNX_MAX_MEMORY); - lynx->state = have_iomappings; - - if (lynx->registers == NULL) { - FAIL("failed to remap registers - card not accessible"); - } - - reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); - /* Fix buggy cards with autoboot pin not tied low: */ - reg_write(lynx, DMA0_CHAN_CTRL, 0); - - sprintf (irq_buf, "%d", dev->irq); - - if (!request_irq(dev->irq, lynx_irq_handler, IRQF_SHARED, - PCILYNX_DRIVER_NAME, lynx)) { - PRINT(KERN_INFO, lynx->id, "allocated interrupt %s", irq_buf); - lynx->state = have_intr; - } else { - FAIL("failed to allocate shared interrupt %s", irq_buf); - } - - /* alloc_pcl return values are not checked, it is expected that the - * provided PCL space is sufficient for the initial allocations */ - lynx->rcv_pcl = alloc_pcl(lynx); - lynx->rcv_pcl_start = alloc_pcl(lynx); - lynx->async.pcl = alloc_pcl(lynx); - lynx->async.pcl_start = alloc_pcl(lynx); - lynx->iso_send.pcl = alloc_pcl(lynx); - lynx->iso_send.pcl_start = alloc_pcl(lynx); - - for (i = 0; i < NUM_ISORCV_PCL; i++) { - lynx->iso_rcv.pcl[i] = alloc_pcl(lynx); - } - lynx->iso_rcv.pcl_start = alloc_pcl(lynx); - - /* all allocations successful - simple init stuff follows */ - - reg_write(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL); - - tasklet_init(&lynx->iso_rcv.tq, (void (*)(unsigned long))iso_rcv_bh, - (unsigned long)lynx); - - spin_lock_init(&lynx->iso_rcv.lock); - - spin_lock_init(&lynx->async.queue_lock); - lynx->async.channel = CHANNEL_ASYNC_SEND; - spin_lock_init(&lynx->iso_send.queue_lock); - lynx->iso_send.channel = CHANNEL_ISO_SEND; - - PRINT(KERN_INFO, lynx->id, "remapped memory spaces reg 0x%p, rom 0x%p, " - "ram 0x%p, aux 0x%p", lynx->registers, lynx->local_rom, - lynx->local_ram, lynx->aux_port); - - /* now, looking for PHY register set */ - if ((get_phy_reg(lynx, 2) & 0xe0) == 0xe0) { - lynx->phyic.reg_1394a = 1; - PRINT(KERN_INFO, lynx->id, - "found 1394a conform PHY (using extended register set)"); - lynx->phyic.vendor = get_phy_vendorid(lynx); - lynx->phyic.product = get_phy_productid(lynx); - } else { - lynx->phyic.reg_1394a = 0; - PRINT(KERN_INFO, lynx->id, "found old 1394 PHY"); - } - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - - INIT_LIST_HEAD(&lynx->async.queue); - INIT_LIST_HEAD(&lynx->async.pcl_queue); - INIT_LIST_HEAD(&lynx->iso_send.queue); - INIT_LIST_HEAD(&lynx->iso_send.pcl_queue); - - pcl.next = pcl_bus(lynx, lynx->rcv_pcl); - put_pcl(lynx, lynx->rcv_pcl_start, &pcl); - - pcl.next = PCL_NEXT_INVALID; - pcl.async_error_next = PCL_NEXT_INVALID; - - pcl.buffer[0].control = PCL_CMD_RCV | 16; -#ifndef __BIG_ENDIAN - pcl.buffer[0].control |= PCL_BIGENDIAN; -#endif - pcl.buffer[1].control = PCL_LAST_BUFF | 4080; - - pcl.buffer[0].pointer = lynx->rcv_page_dma; - pcl.buffer[1].pointer = lynx->rcv_page_dma + 16; - put_pcl(lynx, lynx->rcv_pcl, &pcl); - - pcl.next = pcl_bus(lynx, lynx->async.pcl); - pcl.async_error_next = pcl_bus(lynx, lynx->async.pcl); - put_pcl(lynx, lynx->async.pcl_start, &pcl); - - pcl.next = pcl_bus(lynx, lynx->iso_send.pcl); - pcl.async_error_next = PCL_NEXT_INVALID; - put_pcl(lynx, lynx->iso_send.pcl_start, &pcl); - - pcl.next = PCL_NEXT_INVALID; - pcl.async_error_next = PCL_NEXT_INVALID; - pcl.buffer[0].control = PCL_CMD_RCV | 4; -#ifndef __BIG_ENDIAN - pcl.buffer[0].control |= PCL_BIGENDIAN; -#endif - pcl.buffer[1].control = PCL_LAST_BUFF | 2044; - - for (i = 0; i < NUM_ISORCV_PCL; i++) { - int page = i / ISORCV_PER_PAGE; - int sec = i % ISORCV_PER_PAGE; - - pcl.buffer[0].pointer = lynx->iso_rcv.page_dma[page] - + sec * MAX_ISORCV_SIZE; - pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4; - put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl); - } - - pcli = (u32 *)&pcl; - for (i = 0; i < NUM_ISORCV_PCL; i++) { - pcli[i] = pcl_bus(lynx, lynx->iso_rcv.pcl[i]); - } - put_pcl(lynx, lynx->iso_rcv.pcl_start, &pcl); - - /* FIFO sizes from left to right: ITF=48 ATF=48 GRF=160 */ - reg_write(lynx, FIFO_SIZES, 0x003030a0); - /* 20 byte threshold before triggering PCI transfer */ - reg_write(lynx, DMA_GLOBAL_REGISTER, 0x2<<24); - /* threshold on both send FIFOs before transmitting: - FIFO size - cache line size - 1 */ - i = reg_read(lynx, PCI_LATENCY_CACHELINE) & 0xff; - i = 0x30 - i - 1; - reg_write(lynx, FIFO_XMIT_THRESHOLD, (i << 8) | i); - - reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_1394); - - reg_write(lynx, LINK_INT_ENABLE, LINK_INT_PHY_TIMEOUT - | LINK_INT_PHY_REG_RCVD | LINK_INT_PHY_BUSRESET - | LINK_INT_ISO_STUCK | LINK_INT_ASYNC_STUCK - | LINK_INT_SENT_REJECT | LINK_INT_TX_INVALID_TC - | LINK_INT_GRF_OVERFLOW | LINK_INT_ITF_UNDERFLOW - | LINK_INT_ATF_UNDERFLOW); - - reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ASYNC_RCV), 0); - reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ASYNC_RCV), 0xa<<4); - reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ASYNC_RCV), 0); - reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ASYNC_RCV), - DMA_WORD1_CMP_MATCH_LOCAL_NODE | DMA_WORD1_CMP_MATCH_BROADCAST - | DMA_WORD1_CMP_MATCH_EXACT | DMA_WORD1_CMP_MATCH_BUS_BCAST - | DMA_WORD1_CMP_ENABLE_SELF_ID | DMA_WORD1_CMP_ENABLE_MASTER); - - run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV); - - reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ISO_RCV), 0); - reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ISO_RCV), 0x9<<4); - reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ISO_RCV), 0); - reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0); - - run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, 0, CHANNEL_ISO_RCV); - - reg_write(lynx, LINK_CONTROL, LINK_CONTROL_RCV_CMP_VALID - | LINK_CONTROL_TX_ISO_EN | LINK_CONTROL_RX_ISO_EN - | LINK_CONTROL_TX_ASYNC_EN | LINK_CONTROL_RX_ASYNC_EN - | LINK_CONTROL_RESET_TX | LINK_CONTROL_RESET_RX); - - if (!lynx->phyic.reg_1394a) { - if (!hpsb_disable_irm) { - /* attempt to enable contender bit -FIXME- would this - * work elsewhere? */ - reg_set_bits(lynx, GPIO_CTRL_A, 0x1); - reg_write(lynx, GPIO_DATA_BASE + 0x3c, 0x1); - } - } else { - /* set the contender (if appropriate) and LCtrl bit in the - * extended PHY register set. (Should check that PHY_02_EXTENDED - * is set in register 2?) - */ - i = get_phy_reg(lynx, 4); - i |= PHY_04_LCTRL; - if (hpsb_disable_irm) - i &= ~PHY_04_CONTENDER; - else - i |= PHY_04_CONTENDER; - if (i != -1) set_phy_reg(lynx, 4, i); - } - - if (!skip_eeprom) - { - /* needed for i2c communication with serial eeprom */ - struct i2c_adapter *i2c_ad; - struct i2c_algo_bit_data i2c_adapter_data; - - error = -ENOMEM; - i2c_ad = kzalloc(sizeof(*i2c_ad), GFP_KERNEL); - if (!i2c_ad) FAIL("failed to allocate I2C adapter memory"); - - strlcpy(i2c_ad->name, "PCILynx I2C", sizeof(i2c_ad->name)); - i2c_adapter_data = bit_data; - i2c_ad->algo_data = &i2c_adapter_data; - i2c_adapter_data.data = lynx; - i2c_ad->dev.parent = &dev->dev; - - PRINTD(KERN_DEBUG, lynx->id,"original eeprom control: %d", - reg_read(lynx, SERIAL_EEPROM_CONTROL)); - - /* reset hardware to sane state */ - lynx->i2c_driven_state = 0x00000070; - reg_write(lynx, SERIAL_EEPROM_CONTROL, lynx->i2c_driven_state); - - if (i2c_bit_add_bus(i2c_ad) < 0) - { - kfree(i2c_ad); - error = -ENXIO; - FAIL("unable to register i2c"); - } - else - { - /* do i2c stuff */ - unsigned char i2c_cmd = 0x10; - struct i2c_msg msg[2] = { { 0x50, 0, 1, &i2c_cmd }, - { 0x50, I2C_M_RD, 20, (unsigned char*) lynx->bus_info_block } - }; - - /* we use i2c_transfer because we have no i2c_client - at hand */ - if (i2c_transfer(i2c_ad, msg, 2) < 0) { - PRINT(KERN_ERR, lynx->id, "unable to read bus info block from i2c"); - } else { - PRINT(KERN_INFO, lynx->id, "got bus info block from serial eeprom"); - /* FIXME: probably we should rewrite the max_rec, max_ROM(1394a), - * generation(1394a) and link_spd(1394a) field and recalculate - * the CRC */ - - for (i = 0; i < 5 ; i++) - PRINTD(KERN_DEBUG, lynx->id, "Businfo block quadlet %i: %08x", - i, be32_to_cpu(lynx->bus_info_block[i])); - - /* info_length, crc_length and 1394 magic number to check, if it is really a bus info block */ - if (((be32_to_cpu(lynx->bus_info_block[0]) & 0xffff0000) == 0x04040000) && - (lynx->bus_info_block[1] == IEEE1394_BUSID_MAGIC)) - { - PRINT(KERN_DEBUG, lynx->id, "read a valid bus info block from"); - } else { - kfree(i2c_ad); - error = -ENXIO; - FAIL("read something from serial eeprom, but it does not seem to be a valid bus info block"); - } - - } - - i2c_del_adapter(i2c_ad); - kfree(i2c_ad); - } - } - - host->csr.guid_hi = be32_to_cpu(lynx->bus_info_block[3]); - host->csr.guid_lo = be32_to_cpu(lynx->bus_info_block[4]); - host->csr.cyc_clk_acc = (be32_to_cpu(lynx->bus_info_block[2]) >> 16) & 0xff; - host->csr.max_rec = (be32_to_cpu(lynx->bus_info_block[2]) >> 12) & 0xf; - if (!lynx->phyic.reg_1394a) - host->csr.lnk_spd = (get_phy_reg(lynx, 2) & 0xc0) >> 6; - else - host->csr.lnk_spd = be32_to_cpu(lynx->bus_info_block[2]) & 0x7; - - if (hpsb_add_host(host)) { - error = -ENOMEM; - FAIL("Failed to register host with highlevel"); - } - - lynx->state = is_host; - - return 0; -#undef FAIL -} - - -static struct pci_device_id pci_table[] = { - { - .vendor = PCI_VENDOR_ID_TI, - .device = PCI_DEVICE_ID_TI_PCILYNX, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, - { } /* Terminating entry */ -}; - -static struct pci_driver lynx_pci_driver = { - .name = PCILYNX_DRIVER_NAME, - .id_table = pci_table, - .probe = add_card, - .remove = remove_card, -}; - -static struct hpsb_host_driver lynx_driver = { - .owner = THIS_MODULE, - .name = PCILYNX_DRIVER_NAME, - .set_hw_config_rom = NULL, - .transmit_packet = lynx_transmit, - .devctl = lynx_devctl, - .isoctl = NULL, -}; - -MODULE_AUTHOR("Andreas E. Bombe "); -MODULE_DESCRIPTION("driver for Texas Instruments PCI Lynx IEEE-1394 controller"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("pcilynx"); -MODULE_DEVICE_TABLE(pci, pci_table); - -static int __init pcilynx_init(void) -{ - int ret; - - ret = pci_register_driver(&lynx_pci_driver); - if (ret < 0) { - PRINT_G(KERN_ERR, "PCI module init failed"); - return ret; - } - - return 0; -} - -static void __exit pcilynx_cleanup(void) -{ - pci_unregister_driver(&lynx_pci_driver); -} - - -module_init(pcilynx_init); -module_exit(pcilynx_cleanup); diff --git a/drivers/ieee1394/pcilynx.h b/drivers/ieee1394/pcilynx.h deleted file mode 100644 index 693a169acea3..000000000000 --- a/drivers/ieee1394/pcilynx.h +++ /dev/null @@ -1,468 +0,0 @@ -#ifndef __PCILYNX_H__ -#define __PCILYNX_H__ - - -#define PCILYNX_DRIVER_NAME "pcilynx" -#define PCILYNX_MAJOR 177 - -#define PCILYNX_MINOR_AUX_START 0 -#define PCILYNX_MINOR_ROM_START 16 -#define PCILYNX_MINOR_RAM_START 32 - -#define PCILYNX_MAX_REGISTER 0xfff -#define PCILYNX_MAX_MEMORY 0xffff - -#define PCI_DEVICE_ID_TI_PCILYNX 0x8000 -#define MAX_PCILYNX_CARDS 4 -#define LOCALRAM_SIZE 4096 - -#define NUM_ISORCV_PCL 4 -#define MAX_ISORCV_SIZE 2048 -#define ISORCV_PER_PAGE (PAGE_SIZE / MAX_ISORCV_SIZE) -#define ISORCV_PAGES (NUM_ISORCV_PCL / ISORCV_PER_PAGE) - -#define CHANNEL_LOCALBUS 0 -#define CHANNEL_ASYNC_RCV 1 -#define CHANNEL_ISO_RCV 2 -#define CHANNEL_ASYNC_SEND 3 -#define CHANNEL_ISO_SEND 4 - -#define PCILYNX_CONFIG_ROM_LENGTH 1024 - -typedef int pcl_t; - -struct ti_lynx { - int id; /* sequential card number */ - - spinlock_t lock; - - struct pci_dev *dev; - - struct { - unsigned reg_1394a:1; - u32 vendor; - u32 product; - } phyic; - - enum { clear, have_intr, have_aux_buf, have_pcl_mem, - have_1394_buffers, have_iomappings, is_host } state; - - /* remapped memory spaces */ - void __iomem *registers; - void __iomem *local_rom; - void __iomem *local_ram; - void __iomem *aux_port; - __be32 bus_info_block[5]; - - /* - * use local RAM of LOCALRAM_SIZE bytes for PCLs, which allows for - * LOCALRAM_SIZE * 8 PCLs (each sized 128 bytes); - * the following is an allocation bitmap - */ - u8 pcl_bmap[LOCALRAM_SIZE / 1024]; - - /* point to PCLs memory area if needed */ - void *pcl_mem; - dma_addr_t pcl_mem_dma; - - /* PCLs for local mem / aux transfers */ - pcl_t dmem_pcl; - - /* IEEE-1394 part follows */ - struct hpsb_host *host; - - int phyid, isroot; - int selfid_size; - int phy_reg0; - - spinlock_t phy_reg_lock; - - pcl_t rcv_pcl_start, rcv_pcl; - void *rcv_page; - dma_addr_t rcv_page_dma; - int rcv_active; - - struct lynx_send_data { - pcl_t pcl_start, pcl; - struct list_head queue; - struct list_head pcl_queue; /* this queue contains at most one packet */ - spinlock_t queue_lock; - dma_addr_t header_dma, data_dma; - int channel; - } async, iso_send; - - struct { - pcl_t pcl[NUM_ISORCV_PCL]; - u32 stat[NUM_ISORCV_PCL]; - void *page[ISORCV_PAGES]; - dma_addr_t page_dma[ISORCV_PAGES]; - pcl_t pcl_start; - int chan_count; - int next, last, used, running; - struct tasklet_struct tq; - spinlock_t lock; - } iso_rcv; - - u32 i2c_driven_state; /* the state we currently drive the Serial EEPROM Control register */ -}; - -/* the per-file data structure for mem space access */ -struct memdata { - struct ti_lynx *lynx; - int cid; - atomic_t aux_intr_last_seen; - /* enum values are the same as LBUS_ADDR_SEL_* values below */ - enum { rom = 0x10000, aux = 0x20000, ram = 0 } type; -}; - - - -/* - * Register read and write helper functions. - */ -static inline void reg_write(const struct ti_lynx *lynx, int offset, u32 data) -{ - writel(data, lynx->registers + offset); -} - -static inline u32 reg_read(const struct ti_lynx *lynx, int offset) -{ - return readl(lynx->registers + offset); -} - -static inline void reg_set_bits(const struct ti_lynx *lynx, int offset, - u32 mask) -{ - reg_write(lynx, offset, (reg_read(lynx, offset) | mask)); -} - -static inline void reg_clear_bits(const struct ti_lynx *lynx, int offset, - u32 mask) -{ - reg_write(lynx, offset, (reg_read(lynx, offset) & ~mask)); -} - - - -/* chip register definitions follow */ - -#define PCI_LATENCY_CACHELINE 0x0c - -#define MISC_CONTROL 0x40 -#define MISC_CONTROL_SWRESET (1<<0) - -#define SERIAL_EEPROM_CONTROL 0x44 - -#define PCI_INT_STATUS 0x48 -#define PCI_INT_ENABLE 0x4c -/* status and enable have identical bit numbers */ -#define PCI_INT_INT_PEND (1<<31) -#define PCI_INT_FORCED_INT (1<<30) -#define PCI_INT_SLV_ADR_PERR (1<<28) -#define PCI_INT_SLV_DAT_PERR (1<<27) -#define PCI_INT_MST_DAT_PERR (1<<26) -#define PCI_INT_MST_DEV_TIMEOUT (1<<25) -#define PCI_INT_INTERNAL_SLV_TIMEOUT (1<<23) -#define PCI_INT_AUX_TIMEOUT (1<<18) -#define PCI_INT_AUX_INT (1<<17) -#define PCI_INT_1394 (1<<16) -#define PCI_INT_DMA4_PCL (1<<9) -#define PCI_INT_DMA4_HLT (1<<8) -#define PCI_INT_DMA3_PCL (1<<7) -#define PCI_INT_DMA3_HLT (1<<6) -#define PCI_INT_DMA2_PCL (1<<5) -#define PCI_INT_DMA2_HLT (1<<4) -#define PCI_INT_DMA1_PCL (1<<3) -#define PCI_INT_DMA1_HLT (1<<2) -#define PCI_INT_DMA0_PCL (1<<1) -#define PCI_INT_DMA0_HLT (1<<0) -/* all DMA interrupts combined: */ -#define PCI_INT_DMA_ALL 0x3ff - -#define PCI_INT_DMA_HLT(chan) (1 << (chan * 2)) -#define PCI_INT_DMA_PCL(chan) (1 << (chan * 2 + 1)) - -#define LBUS_ADDR 0xb4 -#define LBUS_ADDR_SEL_RAM (0x0<<16) -#define LBUS_ADDR_SEL_ROM (0x1<<16) -#define LBUS_ADDR_SEL_AUX (0x2<<16) -#define LBUS_ADDR_SEL_ZV (0x3<<16) - -#define GPIO_CTRL_A 0xb8 -#define GPIO_CTRL_B 0xbc -#define GPIO_DATA_BASE 0xc0 - -#define DMA_BREG(base, chan) (base + chan * 0x20) -#define DMA_SREG(base, chan) (base + chan * 0x10) - -#define DMA0_PREV_PCL 0x100 -#define DMA1_PREV_PCL 0x120 -#define DMA2_PREV_PCL 0x140 -#define DMA3_PREV_PCL 0x160 -#define DMA4_PREV_PCL 0x180 -#define DMA_PREV_PCL(chan) (DMA_BREG(DMA0_PREV_PCL, chan)) - -#define DMA0_CURRENT_PCL 0x104 -#define DMA1_CURRENT_PCL 0x124 -#define DMA2_CURRENT_PCL 0x144 -#define DMA3_CURRENT_PCL 0x164 -#define DMA4_CURRENT_PCL 0x184 -#define DMA_CURRENT_PCL(chan) (DMA_BREG(DMA0_CURRENT_PCL, chan)) - -#define DMA0_CHAN_STAT 0x10c -#define DMA1_CHAN_STAT 0x12c -#define DMA2_CHAN_STAT 0x14c -#define DMA3_CHAN_STAT 0x16c -#define DMA4_CHAN_STAT 0x18c -#define DMA_CHAN_STAT(chan) (DMA_BREG(DMA0_CHAN_STAT, chan)) -/* CHAN_STATUS registers share bits */ -#define DMA_CHAN_STAT_SELFID (1<<31) -#define DMA_CHAN_STAT_ISOPKT (1<<30) -#define DMA_CHAN_STAT_PCIERR (1<<29) -#define DMA_CHAN_STAT_PKTERR (1<<28) -#define DMA_CHAN_STAT_PKTCMPL (1<<27) -#define DMA_CHAN_STAT_SPECIALACK (1<<14) - - -#define DMA0_CHAN_CTRL 0x110 -#define DMA1_CHAN_CTRL 0x130 -#define DMA2_CHAN_CTRL 0x150 -#define DMA3_CHAN_CTRL 0x170 -#define DMA4_CHAN_CTRL 0x190 -#define DMA_CHAN_CTRL(chan) (DMA_BREG(DMA0_CHAN_CTRL, chan)) -/* CHAN_CTRL registers share bits */ -#define DMA_CHAN_CTRL_ENABLE (1<<31) -#define DMA_CHAN_CTRL_BUSY (1<<30) -#define DMA_CHAN_CTRL_LINK (1<<29) - -#define DMA0_READY 0x114 -#define DMA1_READY 0x134 -#define DMA2_READY 0x154 -#define DMA3_READY 0x174 -#define DMA4_READY 0x194 -#define DMA_READY(chan) (DMA_BREG(DMA0_READY, chan)) - -#define DMA_GLOBAL_REGISTER 0x908 - -#define FIFO_SIZES 0xa00 - -#define FIFO_CONTROL 0xa10 -#define FIFO_CONTROL_GRF_FLUSH (1<<4) -#define FIFO_CONTROL_ITF_FLUSH (1<<3) -#define FIFO_CONTROL_ATF_FLUSH (1<<2) - -#define FIFO_XMIT_THRESHOLD 0xa14 - -#define DMA0_WORD0_CMP_VALUE 0xb00 -#define DMA1_WORD0_CMP_VALUE 0xb10 -#define DMA2_WORD0_CMP_VALUE 0xb20 -#define DMA3_WORD0_CMP_VALUE 0xb30 -#define DMA4_WORD0_CMP_VALUE 0xb40 -#define DMA_WORD0_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD0_CMP_VALUE, chan)) - -#define DMA0_WORD0_CMP_ENABLE 0xb04 -#define DMA1_WORD0_CMP_ENABLE 0xb14 -#define DMA2_WORD0_CMP_ENABLE 0xb24 -#define DMA3_WORD0_CMP_ENABLE 0xb34 -#define DMA4_WORD0_CMP_ENABLE 0xb44 -#define DMA_WORD0_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD0_CMP_ENABLE,chan)) - -#define DMA0_WORD1_CMP_VALUE 0xb08 -#define DMA1_WORD1_CMP_VALUE 0xb18 -#define DMA2_WORD1_CMP_VALUE 0xb28 -#define DMA3_WORD1_CMP_VALUE 0xb38 -#define DMA4_WORD1_CMP_VALUE 0xb48 -#define DMA_WORD1_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD1_CMP_VALUE, chan)) - -#define DMA0_WORD1_CMP_ENABLE 0xb0c -#define DMA1_WORD1_CMP_ENABLE 0xb1c -#define DMA2_WORD1_CMP_ENABLE 0xb2c -#define DMA3_WORD1_CMP_ENABLE 0xb3c -#define DMA4_WORD1_CMP_ENABLE 0xb4c -#define DMA_WORD1_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD1_CMP_ENABLE,chan)) -/* word 1 compare enable flags */ -#define DMA_WORD1_CMP_MATCH_OTHERBUS (1<<15) -#define DMA_WORD1_CMP_MATCH_BROADCAST (1<<14) -#define DMA_WORD1_CMP_MATCH_BUS_BCAST (1<<13) -#define DMA_WORD1_CMP_MATCH_LOCAL_NODE (1<<12) -#define DMA_WORD1_CMP_MATCH_EXACT (1<<11) -#define DMA_WORD1_CMP_ENABLE_SELF_ID (1<<10) -#define DMA_WORD1_CMP_ENABLE_MASTER (1<<8) - -#define LINK_ID 0xf00 -#define LINK_ID_BUS(id) (id<<22) -#define LINK_ID_NODE(id) (id<<16) - -#define LINK_CONTROL 0xf04 -#define LINK_CONTROL_BUSY (1<<29) -#define LINK_CONTROL_TX_ISO_EN (1<<26) -#define LINK_CONTROL_RX_ISO_EN (1<<25) -#define LINK_CONTROL_TX_ASYNC_EN (1<<24) -#define LINK_CONTROL_RX_ASYNC_EN (1<<23) -#define LINK_CONTROL_RESET_TX (1<<21) -#define LINK_CONTROL_RESET_RX (1<<20) -#define LINK_CONTROL_CYCMASTER (1<<11) -#define LINK_CONTROL_CYCSOURCE (1<<10) -#define LINK_CONTROL_CYCTIMEREN (1<<9) -#define LINK_CONTROL_RCV_CMP_VALID (1<<7) -#define LINK_CONTROL_SNOOP_ENABLE (1<<6) - -#define CYCLE_TIMER 0xf08 - -#define LINK_PHY 0xf0c -#define LINK_PHY_READ (1<<31) -#define LINK_PHY_WRITE (1<<30) -#define LINK_PHY_ADDR(addr) (addr<<24) -#define LINK_PHY_WDATA(data) (data<<16) -#define LINK_PHY_RADDR(addr) (addr<<8) - - -#define LINK_INT_STATUS 0xf14 -#define LINK_INT_ENABLE 0xf18 -/* status and enable have identical bit numbers */ -#define LINK_INT_LINK_INT (1<<31) -#define LINK_INT_PHY_TIMEOUT (1<<30) -#define LINK_INT_PHY_REG_RCVD (1<<29) -#define LINK_INT_PHY_BUSRESET (1<<28) -#define LINK_INT_TX_RDY (1<<26) -#define LINK_INT_RX_DATA_RDY (1<<25) -#define LINK_INT_ISO_STUCK (1<<20) -#define LINK_INT_ASYNC_STUCK (1<<19) -#define LINK_INT_SENT_REJECT (1<<17) -#define LINK_INT_HDR_ERR (1<<16) -#define LINK_INT_TX_INVALID_TC (1<<15) -#define LINK_INT_CYC_SECOND (1<<11) -#define LINK_INT_CYC_START (1<<10) -#define LINK_INT_CYC_DONE (1<<9) -#define LINK_INT_CYC_PENDING (1<<8) -#define LINK_INT_CYC_LOST (1<<7) -#define LINK_INT_CYC_ARB_FAILED (1<<6) -#define LINK_INT_GRF_OVERFLOW (1<<5) -#define LINK_INT_ITF_UNDERFLOW (1<<4) -#define LINK_INT_ATF_UNDERFLOW (1<<3) -#define LINK_INT_ISOARB_FAILED (1<<0) - -/* PHY specifics */ -#define PHY_VENDORID_TI 0x800028 -#define PHY_PRODUCTID_TSB41LV03 0x000000 - - -/* this is the physical layout of a PCL, its size is 128 bytes */ -struct ti_pcl { - u32 next; - u32 async_error_next; - u32 user_data; - u32 pcl_status; - u32 remaining_transfer_count; - u32 next_data_buffer; - struct { - u32 control; - u32 pointer; - } buffer[13] __attribute__ ((packed)); -} __attribute__ ((packed)); - -#include -#define pcloffs(MEMBER) (offsetof(struct ti_pcl, MEMBER)) - - -static inline void put_pcl(const struct ti_lynx *lynx, pcl_t pclid, - const struct ti_pcl *pcl) -{ - memcpy_le32((u32 *)(lynx->pcl_mem + pclid * sizeof(struct ti_pcl)), - (u32 *)pcl, sizeof(struct ti_pcl)); -} - -static inline void get_pcl(const struct ti_lynx *lynx, pcl_t pclid, - struct ti_pcl *pcl) -{ - memcpy_le32((u32 *)pcl, - (u32 *)(lynx->pcl_mem + pclid * sizeof(struct ti_pcl)), - sizeof(struct ti_pcl)); -} - -static inline u32 pcl_bus(const struct ti_lynx *lynx, pcl_t pclid) -{ - return lynx->pcl_mem_dma + pclid * sizeof(struct ti_pcl); -} - - -#if defined (__BIG_ENDIAN) -typedef struct ti_pcl pcltmp_t; - -static inline struct ti_pcl *edit_pcl(const struct ti_lynx *lynx, pcl_t pclid, - pcltmp_t *tmp) -{ - get_pcl(lynx, pclid, tmp); - return tmp; -} - -static inline void commit_pcl(const struct ti_lynx *lynx, pcl_t pclid, - pcltmp_t *tmp) -{ - put_pcl(lynx, pclid, tmp); -} - -#else -typedef int pcltmp_t; /* just a dummy */ - -static inline struct ti_pcl *edit_pcl(const struct ti_lynx *lynx, pcl_t pclid, - pcltmp_t *tmp) -{ - return lynx->pcl_mem + pclid * sizeof(struct ti_pcl); -} - -static inline void commit_pcl(const struct ti_lynx *lynx, pcl_t pclid, - pcltmp_t *tmp) -{ -} -#endif - - -static inline void run_sub_pcl(const struct ti_lynx *lynx, pcl_t pclid, int idx, - int dmachan) -{ - reg_write(lynx, DMA0_CURRENT_PCL + dmachan * 0x20, - pcl_bus(lynx, pclid) + idx * 4); - reg_write(lynx, DMA0_CHAN_CTRL + dmachan * 0x20, - DMA_CHAN_CTRL_ENABLE | DMA_CHAN_CTRL_LINK); -} - -static inline void run_pcl(const struct ti_lynx *lynx, pcl_t pclid, int dmachan) -{ - run_sub_pcl(lynx, pclid, 0, dmachan); -} - -#define PCL_NEXT_INVALID (1<<0) - -/* transfer commands */ -#define PCL_CMD_RCV (0x1<<24) -#define PCL_CMD_RCV_AND_UPDATE (0xa<<24) -#define PCL_CMD_XMT (0x2<<24) -#define PCL_CMD_UNFXMT (0xc<<24) -#define PCL_CMD_PCI_TO_LBUS (0x8<<24) -#define PCL_CMD_LBUS_TO_PCI (0x9<<24) - -/* aux commands */ -#define PCL_CMD_NOP (0x0<<24) -#define PCL_CMD_LOAD (0x3<<24) -#define PCL_CMD_STOREQ (0x4<<24) -#define PCL_CMD_STORED (0xb<<24) -#define PCL_CMD_STORE0 (0x5<<24) -#define PCL_CMD_STORE1 (0x6<<24) -#define PCL_CMD_COMPARE (0xe<<24) -#define PCL_CMD_SWAP_COMPARE (0xf<<24) -#define PCL_CMD_ADD (0xd<<24) -#define PCL_CMD_BRANCH (0x7<<24) - -/* BRANCH condition codes */ -#define PCL_COND_DMARDY_SET (0x1<<20) -#define PCL_COND_DMARDY_CLEAR (0x2<<20) - -#define PCL_GEN_INTR (1<<19) -#define PCL_LAST_BUFF (1<<18) -#define PCL_LAST_CMD (PCL_LAST_BUFF) -#define PCL_WAITSTAT (1<<17) -#define PCL_BIGENDIAN (1<<16) -#define PCL_ISOMODE (1<<12) - -#endif diff --git a/drivers/ieee1394/raw1394-private.h b/drivers/ieee1394/raw1394-private.h deleted file mode 100644 index 7a225a405987..000000000000 --- a/drivers/ieee1394/raw1394-private.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef IEEE1394_RAW1394_PRIVATE_H -#define IEEE1394_RAW1394_PRIVATE_H - -/* header for definitions that are private to the raw1394 driver - and not visible to user-space */ - -#define RAW1394_DEVICE_MAJOR 171 -#define RAW1394_DEVICE_NAME "raw1394" - -#define RAW1394_MAX_USER_CSR_DIRS 16 - -struct iso_block_store { - atomic_t refcount; - size_t data_size; - quadlet_t data[0]; -}; - -enum raw1394_iso_state { RAW1394_ISO_INACTIVE = 0, - RAW1394_ISO_RECV = 1, - RAW1394_ISO_XMIT = 2 }; - -struct file_info { - struct list_head list; - - struct mutex state_mutex; - enum { opened, initialized, connected } state; - unsigned int protocol_version; - - struct hpsb_host *host; - - struct list_head req_pending; /* protected by reqlists_lock */ - struct list_head req_complete; /* protected by reqlists_lock */ - spinlock_t reqlists_lock; - wait_queue_head_t wait_complete; - - struct list_head addr_list; /* protected by host_info_lock */ - - u8 __user *fcp_buffer; - - u8 notification; /* (busreset-notification) RAW1394_NOTIFY_OFF/ON */ - - /* new rawiso API */ - enum raw1394_iso_state iso_state; - struct hpsb_iso *iso_handle; - - /* User space's CSR1212 dynamic ConfigROM directories */ - struct csr1212_keyval *csr1212_dirs[RAW1394_MAX_USER_CSR_DIRS]; - - /* Legacy ConfigROM update flag */ - u8 cfgrom_upd; -}; - -struct arm_addr { - struct list_head addr_list; /* file_info list */ - u64 start, end; - u64 arm_tag; - u8 access_rights; - u8 notification_options; - u8 client_transactions; - u64 recvb; - u16 rec_length; - u8 *addr_space_buffer; /* accessed by read/write/lock requests */ -}; - -struct pending_request { - struct list_head list; - struct file_info *file_info; - struct hpsb_packet *packet; - struct iso_block_store *ibs; - quadlet_t *data; - int free_data; - struct raw1394_request req; -}; - -struct host_info { - struct list_head list; - struct hpsb_host *host; - struct list_head file_info_list; /* protected by host_info_lock */ -}; - -#endif /* IEEE1394_RAW1394_PRIVATE_H */ diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c deleted file mode 100644 index f3401427404c..000000000000 --- a/drivers/ieee1394/raw1394.c +++ /dev/null @@ -1,3096 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * Raw interface to the bus - * - * Copyright (C) 1999, 2000 Andreas E. Bombe - * 2001, 2002 Manfred Weihs - * 2002 Christian Toegel - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - * - * - * Contributions: - * - * Manfred Weihs - * configuration ROM manipulation - * address range mapping - * adaptation for new (transparent) loopback mechanism - * sending of arbitrary async packets - * Christian Toegel - * address range mapping - * lock64 request - * transmit physical packet - * busreset notification control (switch on/off) - * busreset with selection of type (short/long) - * request_reply - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "csr1212.h" -#include "highlevel.h" -#include "hosts.h" -#include "ieee1394.h" -#include "ieee1394_core.h" -#include "ieee1394_hotplug.h" -#include "ieee1394_transactions.h" -#include "ieee1394_types.h" -#include "iso.h" -#include "nodemgr.h" -#include "raw1394.h" -#include "raw1394-private.h" - -#define int2ptr(x) ((void __user *)(unsigned long)x) -#define ptr2int(x) ((u64)(unsigned long)(void __user *)x) - -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG -#define RAW1394_DEBUG -#endif - -#ifdef RAW1394_DEBUG -#define DBGMSG(fmt, args...) \ -printk(KERN_INFO "raw1394:" fmt "\n" , ## args) -#else -#define DBGMSG(fmt, args...) do {} while (0) -#endif - -static LIST_HEAD(host_info_list); -static int host_count; -static DEFINE_SPINLOCK(host_info_lock); -static atomic_t internal_generation = ATOMIC_INIT(0); - -static atomic_t iso_buffer_size; -static const int iso_buffer_max = 4 * 1024 * 1024; /* 4 MB */ - -static struct hpsb_highlevel raw1394_highlevel; - -static int arm_read(struct hpsb_host *host, int nodeid, quadlet_t * buffer, - u64 addr, size_t length, u16 flags); -static int arm_write(struct hpsb_host *host, int nodeid, int destid, - quadlet_t * data, u64 addr, size_t length, u16 flags); -static int arm_lock(struct hpsb_host *host, int nodeid, quadlet_t * store, - u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, - u16 flags); -static int arm_lock64(struct hpsb_host *host, int nodeid, octlet_t * store, - u64 addr, octlet_t data, octlet_t arg, int ext_tcode, - u16 flags); -static const struct hpsb_address_ops arm_ops = { - .read = arm_read, - .write = arm_write, - .lock = arm_lock, - .lock64 = arm_lock64, -}; - -static void queue_complete_cb(struct pending_request *req); - -static struct pending_request *__alloc_pending_request(gfp_t flags) -{ - struct pending_request *req; - - req = kzalloc(sizeof(*req), flags); - if (req) - INIT_LIST_HEAD(&req->list); - - return req; -} - -static inline struct pending_request *alloc_pending_request(void) -{ - return __alloc_pending_request(GFP_KERNEL); -} - -static void free_pending_request(struct pending_request *req) -{ - if (req->ibs) { - if (atomic_dec_and_test(&req->ibs->refcount)) { - atomic_sub(req->ibs->data_size, &iso_buffer_size); - kfree(req->ibs); - } - } else if (req->free_data) { - kfree(req->data); - } - hpsb_free_packet(req->packet); - kfree(req); -} - -/* fi->reqlists_lock must be taken */ -static void __queue_complete_req(struct pending_request *req) -{ - struct file_info *fi = req->file_info; - - list_move_tail(&req->list, &fi->req_complete); - wake_up(&fi->wait_complete); -} - -static void queue_complete_req(struct pending_request *req) -{ - unsigned long flags; - struct file_info *fi = req->file_info; - - spin_lock_irqsave(&fi->reqlists_lock, flags); - __queue_complete_req(req); - spin_unlock_irqrestore(&fi->reqlists_lock, flags); -} - -static void queue_complete_cb(struct pending_request *req) -{ - struct hpsb_packet *packet = req->packet; - int rcode = (packet->header[1] >> 12) & 0xf; - - switch (packet->ack_code) { - case ACKX_NONE: - case ACKX_SEND_ERROR: - req->req.error = RAW1394_ERROR_SEND_ERROR; - break; - case ACKX_ABORTED: - req->req.error = RAW1394_ERROR_ABORTED; - break; - case ACKX_TIMEOUT: - req->req.error = RAW1394_ERROR_TIMEOUT; - break; - default: - req->req.error = (packet->ack_code << 16) | rcode; - break; - } - - if (!((packet->ack_code == ACK_PENDING) && (rcode == RCODE_COMPLETE))) { - req->req.length = 0; - } - - if ((req->req.type == RAW1394_REQ_ASYNC_READ) || - (req->req.type == RAW1394_REQ_ASYNC_WRITE) || - (req->req.type == RAW1394_REQ_ASYNC_STREAM) || - (req->req.type == RAW1394_REQ_LOCK) || - (req->req.type == RAW1394_REQ_LOCK64)) - hpsb_free_tlabel(packet); - - queue_complete_req(req); -} - -static void add_host(struct hpsb_host *host) -{ - struct host_info *hi; - unsigned long flags; - - hi = kmalloc(sizeof(*hi), GFP_KERNEL); - - if (hi) { - INIT_LIST_HEAD(&hi->list); - hi->host = host; - INIT_LIST_HEAD(&hi->file_info_list); - - spin_lock_irqsave(&host_info_lock, flags); - list_add_tail(&hi->list, &host_info_list); - host_count++; - spin_unlock_irqrestore(&host_info_lock, flags); - } - - atomic_inc(&internal_generation); -} - -static struct host_info *find_host_info(struct hpsb_host *host) -{ - struct host_info *hi; - - list_for_each_entry(hi, &host_info_list, list) - if (hi->host == host) - return hi; - - return NULL; -} - -static void remove_host(struct hpsb_host *host) -{ - struct host_info *hi; - unsigned long flags; - - spin_lock_irqsave(&host_info_lock, flags); - hi = find_host_info(host); - - if (hi != NULL) { - list_del(&hi->list); - host_count--; - /* - FIXME: address ranges should be removed - and fileinfo states should be initialized - (including setting generation to - internal-generation ...) - */ - } - spin_unlock_irqrestore(&host_info_lock, flags); - - if (hi == NULL) { - printk(KERN_ERR "raw1394: attempt to remove unknown host " - "0x%p\n", host); - return; - } - - kfree(hi); - - atomic_inc(&internal_generation); -} - -static void host_reset(struct hpsb_host *host) -{ - unsigned long flags; - struct host_info *hi; - struct file_info *fi; - struct pending_request *req; - - spin_lock_irqsave(&host_info_lock, flags); - hi = find_host_info(host); - - if (hi != NULL) { - list_for_each_entry(fi, &hi->file_info_list, list) { - if (fi->notification == RAW1394_NOTIFY_ON) { - req = __alloc_pending_request(GFP_ATOMIC); - - if (req != NULL) { - req->file_info = fi; - req->req.type = RAW1394_REQ_BUS_RESET; - req->req.generation = - get_hpsb_generation(host); - req->req.misc = (host->node_id << 16) - | host->node_count; - if (fi->protocol_version > 3) { - req->req.misc |= - (NODEID_TO_NODE - (host->irm_id) - << 8); - } - - queue_complete_req(req); - } - } - } - } - spin_unlock_irqrestore(&host_info_lock, flags); -} - -static void fcp_request(struct hpsb_host *host, int nodeid, int direction, - int cts, u8 * data, size_t length) -{ - unsigned long flags; - struct host_info *hi; - struct file_info *fi; - struct pending_request *req, *req_next; - struct iso_block_store *ibs = NULL; - LIST_HEAD(reqs); - - if ((atomic_read(&iso_buffer_size) + length) > iso_buffer_max) { - HPSB_INFO("dropped fcp request"); - return; - } - - spin_lock_irqsave(&host_info_lock, flags); - hi = find_host_info(host); - - if (hi != NULL) { - list_for_each_entry(fi, &hi->file_info_list, list) { - if (!fi->fcp_buffer) - continue; - - req = __alloc_pending_request(GFP_ATOMIC); - if (!req) - break; - - if (!ibs) { - ibs = kmalloc(sizeof(*ibs) + length, - GFP_ATOMIC); - if (!ibs) { - kfree(req); - break; - } - - atomic_add(length, &iso_buffer_size); - atomic_set(&ibs->refcount, 0); - ibs->data_size = length; - memcpy(ibs->data, data, length); - } - - atomic_inc(&ibs->refcount); - - req->file_info = fi; - req->ibs = ibs; - req->data = ibs->data; - req->req.type = RAW1394_REQ_FCP_REQUEST; - req->req.generation = get_hpsb_generation(host); - req->req.misc = nodeid | (direction << 16); - req->req.recvb = ptr2int(fi->fcp_buffer); - req->req.length = length; - - list_add_tail(&req->list, &reqs); - } - } - spin_unlock_irqrestore(&host_info_lock, flags); - - list_for_each_entry_safe(req, req_next, &reqs, list) - queue_complete_req(req); -} - -#ifdef CONFIG_COMPAT -struct compat_raw1394_req { - __u32 type; - __s32 error; - __u32 misc; - - __u32 generation; - __u32 length; - - __u64 address; - - __u64 tag; - - __u64 sendb; - __u64 recvb; -} -#if defined(CONFIG_X86_64) || defined(CONFIG_IA64) -__attribute__((packed)) -#endif -; - -static const char __user *raw1394_compat_write(const char __user *buf) -{ - struct compat_raw1394_req __user *cr = (typeof(cr)) buf; - struct raw1394_request __user *r; - - r = compat_alloc_user_space(sizeof(struct raw1394_request)); - -#define C(x) __copy_in_user(&r->x, &cr->x, sizeof(r->x)) - - if (copy_in_user(r, cr, sizeof(struct compat_raw1394_req)) || - C(address) || - C(tag) || - C(sendb) || - C(recvb)) - return (__force const char __user *)ERR_PTR(-EFAULT); - - return (const char __user *)r; -} -#undef C - -#define P(x) __put_user(r->x, &cr->x) - -static int -raw1394_compat_read(const char __user *buf, struct raw1394_request *r) -{ - struct compat_raw1394_req __user *cr = (typeof(cr)) buf; - - if (!access_ok(VERIFY_WRITE, cr, sizeof(struct compat_raw1394_req)) || - P(type) || - P(error) || - P(misc) || - P(generation) || - P(length) || - P(address) || - P(tag) || - P(sendb) || - P(recvb)) - return -EFAULT; - - return sizeof(struct compat_raw1394_req); -} -#undef P - -#endif - -/* get next completed request (caller must hold fi->reqlists_lock) */ -static inline struct pending_request *__next_complete_req(struct file_info *fi) -{ - struct list_head *lh; - struct pending_request *req = NULL; - - if (!list_empty(&fi->req_complete)) { - lh = fi->req_complete.next; - list_del(lh); - req = list_entry(lh, struct pending_request, list); - } - return req; -} - -/* atomically get next completed request */ -static struct pending_request *next_complete_req(struct file_info *fi) -{ - unsigned long flags; - struct pending_request *req; - - spin_lock_irqsave(&fi->reqlists_lock, flags); - req = __next_complete_req(fi); - spin_unlock_irqrestore(&fi->reqlists_lock, flags); - return req; -} - -static ssize_t raw1394_read(struct file *file, char __user * buffer, - size_t count, loff_t * offset_is_ignored) -{ - struct file_info *fi = file->private_data; - struct pending_request *req; - ssize_t ret; - -#ifdef CONFIG_COMPAT - if (count == sizeof(struct compat_raw1394_req)) { - /* ok */ - } else -#endif - if (count != sizeof(struct raw1394_request)) { - return -EINVAL; - } - - if (!access_ok(VERIFY_WRITE, buffer, count)) { - return -EFAULT; - } - - if (file->f_flags & O_NONBLOCK) { - if (!(req = next_complete_req(fi))) - return -EAGAIN; - } else { - /* - * NB: We call the macro wait_event_interruptible() with a - * condition argument with side effect. This is only possible - * because the side effect does not occur until the condition - * became true, and wait_event_interruptible() won't evaluate - * the condition again after that. - */ - if (wait_event_interruptible(fi->wait_complete, - (req = next_complete_req(fi)))) - return -ERESTARTSYS; - } - - if (req->req.length) { - if (copy_to_user(int2ptr(req->req.recvb), req->data, - req->req.length)) { - req->req.error = RAW1394_ERROR_MEMFAULT; - } - } - -#ifdef CONFIG_COMPAT - if (count == sizeof(struct compat_raw1394_req) && - sizeof(struct compat_raw1394_req) != - sizeof(struct raw1394_request)) { - ret = raw1394_compat_read(buffer, &req->req); - } else -#endif - { - if (copy_to_user(buffer, &req->req, sizeof(req->req))) { - ret = -EFAULT; - goto out; - } - ret = (ssize_t) sizeof(struct raw1394_request); - } - out: - free_pending_request(req); - return ret; -} - -static int state_opened(struct file_info *fi, struct pending_request *req) -{ - if (req->req.type == RAW1394_REQ_INITIALIZE) { - switch (req->req.misc) { - case RAW1394_KERNELAPI_VERSION: - case 3: - fi->state = initialized; - fi->protocol_version = req->req.misc; - req->req.error = RAW1394_ERROR_NONE; - req->req.generation = atomic_read(&internal_generation); - break; - - default: - req->req.error = RAW1394_ERROR_COMPAT; - req->req.misc = RAW1394_KERNELAPI_VERSION; - } - } else { - req->req.error = RAW1394_ERROR_STATE_ORDER; - } - - req->req.length = 0; - queue_complete_req(req); - return 0; -} - -static int state_initialized(struct file_info *fi, struct pending_request *req) -{ - unsigned long flags; - struct host_info *hi; - struct raw1394_khost_list *khl; - - if (req->req.generation != atomic_read(&internal_generation)) { - req->req.error = RAW1394_ERROR_GENERATION; - req->req.generation = atomic_read(&internal_generation); - req->req.length = 0; - queue_complete_req(req); - return 0; - } - - switch (req->req.type) { - case RAW1394_REQ_LIST_CARDS: - spin_lock_irqsave(&host_info_lock, flags); - khl = kmalloc(sizeof(*khl) * host_count, GFP_ATOMIC); - - if (khl) { - req->req.misc = host_count; - req->data = (quadlet_t *) khl; - - list_for_each_entry(hi, &host_info_list, list) { - khl->nodes = hi->host->node_count; - strcpy(khl->name, hi->host->driver->name); - khl++; - } - } - spin_unlock_irqrestore(&host_info_lock, flags); - - if (khl) { - req->req.error = RAW1394_ERROR_NONE; - req->req.length = min(req->req.length, - (u32) (sizeof - (struct raw1394_khost_list) - * req->req.misc)); - req->free_data = 1; - } else { - return -ENOMEM; - } - break; - - case RAW1394_REQ_SET_CARD: - spin_lock_irqsave(&host_info_lock, flags); - if (req->req.misc >= host_count) { - req->req.error = RAW1394_ERROR_INVALID_ARG; - goto out_set_card; - } - list_for_each_entry(hi, &host_info_list, list) - if (!req->req.misc--) - break; - get_device(&hi->host->device); /* FIXME handle failure case */ - list_add_tail(&fi->list, &hi->file_info_list); - - /* prevent unloading of the host's low-level driver */ - if (!try_module_get(hi->host->driver->owner)) { - req->req.error = RAW1394_ERROR_ABORTED; - goto out_set_card; - } - WARN_ON(fi->host); - fi->host = hi->host; - fi->state = connected; - - req->req.error = RAW1394_ERROR_NONE; - req->req.generation = get_hpsb_generation(fi->host); - req->req.misc = (fi->host->node_id << 16) - | fi->host->node_count; - if (fi->protocol_version > 3) - req->req.misc |= NODEID_TO_NODE(fi->host->irm_id) << 8; -out_set_card: - spin_unlock_irqrestore(&host_info_lock, flags); - - req->req.length = 0; - break; - - default: - req->req.error = RAW1394_ERROR_STATE_ORDER; - req->req.length = 0; - break; - } - - queue_complete_req(req); - return 0; -} - -static void handle_fcp_listen(struct file_info *fi, struct pending_request *req) -{ - if (req->req.misc) { - if (fi->fcp_buffer) { - req->req.error = RAW1394_ERROR_ALREADY; - } else { - fi->fcp_buffer = int2ptr(req->req.recvb); - } - } else { - if (!fi->fcp_buffer) { - req->req.error = RAW1394_ERROR_ALREADY; - } else { - fi->fcp_buffer = NULL; - } - } - - req->req.length = 0; - queue_complete_req(req); -} - -static int handle_async_request(struct file_info *fi, - struct pending_request *req, int node) -{ - unsigned long flags; - struct hpsb_packet *packet = NULL; - u64 addr = req->req.address & 0xffffffffffffULL; - - switch (req->req.type) { - case RAW1394_REQ_ASYNC_READ: - DBGMSG("read_request called"); - packet = - hpsb_make_readpacket(fi->host, node, addr, req->req.length); - - if (!packet) - return -ENOMEM; - - if (req->req.length == 4) - req->data = &packet->header[3]; - else - req->data = packet->data; - - break; - - case RAW1394_REQ_ASYNC_WRITE: - DBGMSG("write_request called"); - - packet = hpsb_make_writepacket(fi->host, node, addr, NULL, - req->req.length); - if (!packet) - return -ENOMEM; - - if (req->req.length == 4) { - if (copy_from_user - (&packet->header[3], int2ptr(req->req.sendb), - req->req.length)) - req->req.error = RAW1394_ERROR_MEMFAULT; - } else { - if (copy_from_user - (packet->data, int2ptr(req->req.sendb), - req->req.length)) - req->req.error = RAW1394_ERROR_MEMFAULT; - } - - req->req.length = 0; - break; - - case RAW1394_REQ_ASYNC_STREAM: - DBGMSG("stream_request called"); - - packet = - hpsb_make_streampacket(fi->host, NULL, req->req.length, - node & 0x3f /*channel */ , - (req->req.misc >> 16) & 0x3, - req->req.misc & 0xf); - if (!packet) - return -ENOMEM; - - if (copy_from_user(packet->data, int2ptr(req->req.sendb), - req->req.length)) - req->req.error = RAW1394_ERROR_MEMFAULT; - - req->req.length = 0; - break; - - case RAW1394_REQ_LOCK: - DBGMSG("lock_request called"); - if ((req->req.misc == EXTCODE_FETCH_ADD) - || (req->req.misc == EXTCODE_LITTLE_ADD)) { - if (req->req.length != 4) { - req->req.error = RAW1394_ERROR_INVALID_ARG; - break; - } - } else { - if (req->req.length != 8) { - req->req.error = RAW1394_ERROR_INVALID_ARG; - break; - } - } - - packet = hpsb_make_lockpacket(fi->host, node, addr, - req->req.misc, NULL, 0); - if (!packet) - return -ENOMEM; - - if (copy_from_user(packet->data, int2ptr(req->req.sendb), - req->req.length)) { - req->req.error = RAW1394_ERROR_MEMFAULT; - break; - } - - req->data = packet->data; - req->req.length = 4; - break; - - case RAW1394_REQ_LOCK64: - DBGMSG("lock64_request called"); - if ((req->req.misc == EXTCODE_FETCH_ADD) - || (req->req.misc == EXTCODE_LITTLE_ADD)) { - if (req->req.length != 8) { - req->req.error = RAW1394_ERROR_INVALID_ARG; - break; - } - } else { - if (req->req.length != 16) { - req->req.error = RAW1394_ERROR_INVALID_ARG; - break; - } - } - packet = hpsb_make_lock64packet(fi->host, node, addr, - req->req.misc, NULL, 0); - if (!packet) - return -ENOMEM; - - if (copy_from_user(packet->data, int2ptr(req->req.sendb), - req->req.length)) { - req->req.error = RAW1394_ERROR_MEMFAULT; - break; - } - - req->data = packet->data; - req->req.length = 8; - break; - - default: - req->req.error = RAW1394_ERROR_STATE_ORDER; - } - - req->packet = packet; - - if (req->req.error) { - req->req.length = 0; - queue_complete_req(req); - return 0; - } - - hpsb_set_packet_complete_task(packet, - (void (*)(void *))queue_complete_cb, req); - - spin_lock_irqsave(&fi->reqlists_lock, flags); - list_add_tail(&req->list, &fi->req_pending); - spin_unlock_irqrestore(&fi->reqlists_lock, flags); - - packet->generation = req->req.generation; - - if (hpsb_send_packet(packet) < 0) { - req->req.error = RAW1394_ERROR_SEND_ERROR; - req->req.length = 0; - hpsb_free_tlabel(packet); - queue_complete_req(req); - } - return 0; -} - -static int handle_async_send(struct file_info *fi, struct pending_request *req) -{ - unsigned long flags; - struct hpsb_packet *packet; - int header_length = req->req.misc & 0xffff; - int expect_response = req->req.misc >> 16; - size_t data_size; - - if (header_length > req->req.length || header_length < 12 || - header_length > FIELD_SIZEOF(struct hpsb_packet, header)) { - req->req.error = RAW1394_ERROR_INVALID_ARG; - req->req.length = 0; - queue_complete_req(req); - return 0; - } - - data_size = req->req.length - header_length; - packet = hpsb_alloc_packet(data_size); - req->packet = packet; - if (!packet) - return -ENOMEM; - - if (copy_from_user(packet->header, int2ptr(req->req.sendb), - header_length)) { - req->req.error = RAW1394_ERROR_MEMFAULT; - req->req.length = 0; - queue_complete_req(req); - return 0; - } - - if (copy_from_user - (packet->data, int2ptr(req->req.sendb) + header_length, - data_size)) { - req->req.error = RAW1394_ERROR_MEMFAULT; - req->req.length = 0; - queue_complete_req(req); - return 0; - } - - packet->type = hpsb_async; - packet->node_id = packet->header[0] >> 16; - packet->tcode = (packet->header[0] >> 4) & 0xf; - packet->tlabel = (packet->header[0] >> 10) & 0x3f; - packet->host = fi->host; - packet->expect_response = expect_response; - packet->header_size = header_length; - packet->data_size = data_size; - - req->req.length = 0; - hpsb_set_packet_complete_task(packet, - (void (*)(void *))queue_complete_cb, req); - - spin_lock_irqsave(&fi->reqlists_lock, flags); - list_add_tail(&req->list, &fi->req_pending); - spin_unlock_irqrestore(&fi->reqlists_lock, flags); - - /* Update the generation of the packet just before sending. */ - packet->generation = req->req.generation; - - if (hpsb_send_packet(packet) < 0) { - req->req.error = RAW1394_ERROR_SEND_ERROR; - queue_complete_req(req); - } - - return 0; -} - -static int arm_read(struct hpsb_host *host, int nodeid, quadlet_t * buffer, - u64 addr, size_t length, u16 flags) -{ - unsigned long irqflags; - struct pending_request *req; - struct host_info *hi; - struct file_info *fi = NULL; - struct list_head *entry; - struct arm_addr *arm_addr = NULL; - struct arm_request *arm_req = NULL; - struct arm_response *arm_resp = NULL; - int found = 0, size = 0, rcode = -1; - struct arm_request_response *arm_req_resp = NULL; - - DBGMSG("arm_read called by node: %X " - "addr: %4.4x %8.8x length: %Zu", nodeid, - (u16) ((addr >> 32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF), - length); - spin_lock_irqsave(&host_info_lock, irqflags); - hi = find_host_info(host); /* search address-entry */ - if (hi != NULL) { - list_for_each_entry(fi, &hi->file_info_list, list) { - entry = fi->addr_list.next; - while (entry != &(fi->addr_list)) { - arm_addr = - list_entry(entry, struct arm_addr, - addr_list); - if (((arm_addr->start) <= (addr)) - && ((arm_addr->end) >= (addr + length))) { - found = 1; - break; - } - entry = entry->next; - } - if (found) { - break; - } - } - } - rcode = -1; - if (!found) { - printk(KERN_ERR "raw1394: arm_read FAILED addr_entry not found" - " -> rcode_address_error\n"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_ADDRESS_ERROR); - } else { - DBGMSG("arm_read addr_entry FOUND"); - } - if (arm_addr->rec_length < length) { - DBGMSG("arm_read blocklength too big -> rcode_data_error"); - rcode = RCODE_DATA_ERROR; /* hardware error, data is unavailable */ - } - if (rcode == -1) { - if (arm_addr->access_rights & ARM_READ) { - if (!(arm_addr->client_transactions & ARM_READ)) { - memcpy(buffer, - (arm_addr->addr_space_buffer) + (addr - - (arm_addr-> - start)), - length); - DBGMSG("arm_read -> (rcode_complete)"); - rcode = RCODE_COMPLETE; - } - } else { - rcode = RCODE_TYPE_ERROR; /* function not allowed */ - DBGMSG("arm_read -> rcode_type_error (access denied)"); - } - } - if (arm_addr->notification_options & ARM_READ) { - DBGMSG("arm_read -> entering notification-section"); - req = __alloc_pending_request(GFP_ATOMIC); - if (!req) { - DBGMSG("arm_read -> rcode_conflict_error"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request may be retried */ - } - if (rcode == RCODE_COMPLETE) { - size = - sizeof(struct arm_request) + - sizeof(struct arm_response) + - length * sizeof(byte_t) + - sizeof(struct arm_request_response); - } else { - size = - sizeof(struct arm_request) + - sizeof(struct arm_response) + - sizeof(struct arm_request_response); - } - req->data = kmalloc(size, GFP_ATOMIC); - if (!(req->data)) { - free_pending_request(req); - DBGMSG("arm_read -> rcode_conflict_error"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request may be retried */ - } - req->free_data = 1; - req->file_info = fi; - req->req.type = RAW1394_REQ_ARM; - req->req.generation = get_hpsb_generation(host); - req->req.misc = - (((length << 16) & (0xFFFF0000)) | (ARM_READ & 0xFF)); - req->req.tag = arm_addr->arm_tag; - req->req.recvb = arm_addr->recvb; - req->req.length = size; - arm_req_resp = (struct arm_request_response *)(req->data); - arm_req = (struct arm_request *)((byte_t *) (req->data) + - (sizeof - (struct - arm_request_response))); - arm_resp = - (struct arm_response *)((byte_t *) (arm_req) + - (sizeof(struct arm_request))); - arm_req->buffer = NULL; - arm_resp->buffer = NULL; - if (rcode == RCODE_COMPLETE) { - byte_t *buf = - (byte_t *) arm_resp + sizeof(struct arm_response); - memcpy(buf, - (arm_addr->addr_space_buffer) + (addr - - (arm_addr-> - start)), - length); - arm_resp->buffer = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request) + - sizeof(struct arm_response)); - } - arm_resp->buffer_length = - (rcode == RCODE_COMPLETE) ? length : 0; - arm_resp->response_code = rcode; - arm_req->buffer_length = 0; - arm_req->generation = req->req.generation; - arm_req->extended_transaction_code = 0; - arm_req->destination_offset = addr; - arm_req->source_nodeid = nodeid; - arm_req->destination_nodeid = host->node_id; - arm_req->tlabel = (flags >> 10) & 0x3f; - arm_req->tcode = (flags >> 4) & 0x0f; - arm_req_resp->request = int2ptr((arm_addr->recvb) + - sizeof(struct - arm_request_response)); - arm_req_resp->response = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request)); - queue_complete_req(req); - } - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (rcode); -} - -static int arm_write(struct hpsb_host *host, int nodeid, int destid, - quadlet_t * data, u64 addr, size_t length, u16 flags) -{ - unsigned long irqflags; - struct pending_request *req; - struct host_info *hi; - struct file_info *fi = NULL; - struct list_head *entry; - struct arm_addr *arm_addr = NULL; - struct arm_request *arm_req = NULL; - struct arm_response *arm_resp = NULL; - int found = 0, size = 0, rcode = -1; - struct arm_request_response *arm_req_resp = NULL; - - DBGMSG("arm_write called by node: %X " - "addr: %4.4x %8.8x length: %Zu", nodeid, - (u16) ((addr >> 32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF), - length); - spin_lock_irqsave(&host_info_lock, irqflags); - hi = find_host_info(host); /* search address-entry */ - if (hi != NULL) { - list_for_each_entry(fi, &hi->file_info_list, list) { - entry = fi->addr_list.next; - while (entry != &(fi->addr_list)) { - arm_addr = - list_entry(entry, struct arm_addr, - addr_list); - if (((arm_addr->start) <= (addr)) - && ((arm_addr->end) >= (addr + length))) { - found = 1; - break; - } - entry = entry->next; - } - if (found) { - break; - } - } - } - rcode = -1; - if (!found) { - printk(KERN_ERR "raw1394: arm_write FAILED addr_entry not found" - " -> rcode_address_error\n"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_ADDRESS_ERROR); - } else { - DBGMSG("arm_write addr_entry FOUND"); - } - if (arm_addr->rec_length < length) { - DBGMSG("arm_write blocklength too big -> rcode_data_error"); - rcode = RCODE_DATA_ERROR; /* hardware error, data is unavailable */ - } - if (rcode == -1) { - if (arm_addr->access_rights & ARM_WRITE) { - if (!(arm_addr->client_transactions & ARM_WRITE)) { - memcpy((arm_addr->addr_space_buffer) + - (addr - (arm_addr->start)), data, - length); - DBGMSG("arm_write -> (rcode_complete)"); - rcode = RCODE_COMPLETE; - } - } else { - rcode = RCODE_TYPE_ERROR; /* function not allowed */ - DBGMSG("arm_write -> rcode_type_error (access denied)"); - } - } - if (arm_addr->notification_options & ARM_WRITE) { - DBGMSG("arm_write -> entering notification-section"); - req = __alloc_pending_request(GFP_ATOMIC); - if (!req) { - DBGMSG("arm_write -> rcode_conflict_error"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request my be retried */ - } - size = - sizeof(struct arm_request) + sizeof(struct arm_response) + - (length) * sizeof(byte_t) + - sizeof(struct arm_request_response); - req->data = kmalloc(size, GFP_ATOMIC); - if (!(req->data)) { - free_pending_request(req); - DBGMSG("arm_write -> rcode_conflict_error"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request may be retried */ - } - req->free_data = 1; - req->file_info = fi; - req->req.type = RAW1394_REQ_ARM; - req->req.generation = get_hpsb_generation(host); - req->req.misc = - (((length << 16) & (0xFFFF0000)) | (ARM_WRITE & 0xFF)); - req->req.tag = arm_addr->arm_tag; - req->req.recvb = arm_addr->recvb; - req->req.length = size; - arm_req_resp = (struct arm_request_response *)(req->data); - arm_req = (struct arm_request *)((byte_t *) (req->data) + - (sizeof - (struct - arm_request_response))); - arm_resp = - (struct arm_response *)((byte_t *) (arm_req) + - (sizeof(struct arm_request))); - arm_resp->buffer = NULL; - memcpy((byte_t *) arm_resp + sizeof(struct arm_response), - data, length); - arm_req->buffer = int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request) + - sizeof(struct arm_response)); - arm_req->buffer_length = length; - arm_req->generation = req->req.generation; - arm_req->extended_transaction_code = 0; - arm_req->destination_offset = addr; - arm_req->source_nodeid = nodeid; - arm_req->destination_nodeid = destid; - arm_req->tlabel = (flags >> 10) & 0x3f; - arm_req->tcode = (flags >> 4) & 0x0f; - arm_resp->buffer_length = 0; - arm_resp->response_code = rcode; - arm_req_resp->request = int2ptr((arm_addr->recvb) + - sizeof(struct - arm_request_response)); - arm_req_resp->response = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request)); - queue_complete_req(req); - } - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (rcode); -} - -static int arm_lock(struct hpsb_host *host, int nodeid, quadlet_t * store, - u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, - u16 flags) -{ - unsigned long irqflags; - struct pending_request *req; - struct host_info *hi; - struct file_info *fi = NULL; - struct list_head *entry; - struct arm_addr *arm_addr = NULL; - struct arm_request *arm_req = NULL; - struct arm_response *arm_resp = NULL; - int found = 0, size = 0, rcode = -1; - quadlet_t old, new; - struct arm_request_response *arm_req_resp = NULL; - - if (((ext_tcode & 0xFF) == EXTCODE_FETCH_ADD) || - ((ext_tcode & 0xFF) == EXTCODE_LITTLE_ADD)) { - DBGMSG("arm_lock called by node: %X " - "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X", - nodeid, (u16) ((addr >> 32) & 0xFFFF), - (u32) (addr & 0xFFFFFFFF), ext_tcode & 0xFF, - be32_to_cpu(data)); - } else { - DBGMSG("arm_lock called by node: %X " - "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X arg: %8.8X", - nodeid, (u16) ((addr >> 32) & 0xFFFF), - (u32) (addr & 0xFFFFFFFF), ext_tcode & 0xFF, - be32_to_cpu(data), be32_to_cpu(arg)); - } - spin_lock_irqsave(&host_info_lock, irqflags); - hi = find_host_info(host); /* search address-entry */ - if (hi != NULL) { - list_for_each_entry(fi, &hi->file_info_list, list) { - entry = fi->addr_list.next; - while (entry != &(fi->addr_list)) { - arm_addr = - list_entry(entry, struct arm_addr, - addr_list); - if (((arm_addr->start) <= (addr)) - && ((arm_addr->end) >= - (addr + sizeof(*store)))) { - found = 1; - break; - } - entry = entry->next; - } - if (found) { - break; - } - } - } - rcode = -1; - if (!found) { - printk(KERN_ERR "raw1394: arm_lock FAILED addr_entry not found" - " -> rcode_address_error\n"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_ADDRESS_ERROR); - } else { - DBGMSG("arm_lock addr_entry FOUND"); - } - if (rcode == -1) { - if (arm_addr->access_rights & ARM_LOCK) { - if (!(arm_addr->client_transactions & ARM_LOCK)) { - memcpy(&old, - (arm_addr->addr_space_buffer) + (addr - - (arm_addr-> - start)), - sizeof(old)); - switch (ext_tcode) { - case (EXTCODE_MASK_SWAP): - new = data | (old & ~arg); - break; - case (EXTCODE_COMPARE_SWAP): - if (old == arg) { - new = data; - } else { - new = old; - } - break; - case (EXTCODE_FETCH_ADD): - new = - cpu_to_be32(be32_to_cpu(data) + - be32_to_cpu(old)); - break; - case (EXTCODE_LITTLE_ADD): - new = - cpu_to_le32(le32_to_cpu(data) + - le32_to_cpu(old)); - break; - case (EXTCODE_BOUNDED_ADD): - if (old != arg) { - new = - cpu_to_be32(be32_to_cpu - (data) + - be32_to_cpu - (old)); - } else { - new = old; - } - break; - case (EXTCODE_WRAP_ADD): - if (old != arg) { - new = - cpu_to_be32(be32_to_cpu - (data) + - be32_to_cpu - (old)); - } else { - new = data; - } - break; - default: - rcode = RCODE_TYPE_ERROR; /* function not allowed */ - printk(KERN_ERR - "raw1394: arm_lock FAILED " - "ext_tcode not allowed -> rcode_type_error\n"); - break; - } /*switch */ - if (rcode == -1) { - DBGMSG("arm_lock -> (rcode_complete)"); - rcode = RCODE_COMPLETE; - memcpy(store, &old, sizeof(*store)); - memcpy((arm_addr->addr_space_buffer) + - (addr - (arm_addr->start)), - &new, sizeof(*store)); - } - } - } else { - rcode = RCODE_TYPE_ERROR; /* function not allowed */ - DBGMSG("arm_lock -> rcode_type_error (access denied)"); - } - } - if (arm_addr->notification_options & ARM_LOCK) { - byte_t *buf1, *buf2; - DBGMSG("arm_lock -> entering notification-section"); - req = __alloc_pending_request(GFP_ATOMIC); - if (!req) { - DBGMSG("arm_lock -> rcode_conflict_error"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request may be retried */ - } - size = sizeof(struct arm_request) + sizeof(struct arm_response) + 3 * sizeof(*store) + sizeof(struct arm_request_response); /* maximum */ - req->data = kmalloc(size, GFP_ATOMIC); - if (!(req->data)) { - free_pending_request(req); - DBGMSG("arm_lock -> rcode_conflict_error"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request may be retried */ - } - req->free_data = 1; - arm_req_resp = (struct arm_request_response *)(req->data); - arm_req = (struct arm_request *)((byte_t *) (req->data) + - (sizeof - (struct - arm_request_response))); - arm_resp = - (struct arm_response *)((byte_t *) (arm_req) + - (sizeof(struct arm_request))); - buf1 = (byte_t *) arm_resp + sizeof(struct arm_response); - buf2 = buf1 + 2 * sizeof(*store); - if ((ext_tcode == EXTCODE_FETCH_ADD) || - (ext_tcode == EXTCODE_LITTLE_ADD)) { - arm_req->buffer_length = sizeof(*store); - memcpy(buf1, &data, sizeof(*store)); - - } else { - arm_req->buffer_length = 2 * sizeof(*store); - memcpy(buf1, &arg, sizeof(*store)); - memcpy(buf1 + sizeof(*store), &data, sizeof(*store)); - } - if (rcode == RCODE_COMPLETE) { - arm_resp->buffer_length = sizeof(*store); - memcpy(buf2, &old, sizeof(*store)); - } else { - arm_resp->buffer_length = 0; - } - req->file_info = fi; - req->req.type = RAW1394_REQ_ARM; - req->req.generation = get_hpsb_generation(host); - req->req.misc = ((((sizeof(*store)) << 16) & (0xFFFF0000)) | - (ARM_LOCK & 0xFF)); - req->req.tag = arm_addr->arm_tag; - req->req.recvb = arm_addr->recvb; - req->req.length = size; - arm_req->generation = req->req.generation; - arm_req->extended_transaction_code = ext_tcode; - arm_req->destination_offset = addr; - arm_req->source_nodeid = nodeid; - arm_req->destination_nodeid = host->node_id; - arm_req->tlabel = (flags >> 10) & 0x3f; - arm_req->tcode = (flags >> 4) & 0x0f; - arm_resp->response_code = rcode; - arm_req_resp->request = int2ptr((arm_addr->recvb) + - sizeof(struct - arm_request_response)); - arm_req_resp->response = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request)); - arm_req->buffer = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request) + - sizeof(struct arm_response)); - arm_resp->buffer = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request) + - sizeof(struct arm_response) + 2 * sizeof(*store)); - queue_complete_req(req); - } - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (rcode); -} - -static int arm_lock64(struct hpsb_host *host, int nodeid, octlet_t * store, - u64 addr, octlet_t data, octlet_t arg, int ext_tcode, - u16 flags) -{ - unsigned long irqflags; - struct pending_request *req; - struct host_info *hi; - struct file_info *fi = NULL; - struct list_head *entry; - struct arm_addr *arm_addr = NULL; - struct arm_request *arm_req = NULL; - struct arm_response *arm_resp = NULL; - int found = 0, size = 0, rcode = -1; - octlet_t old, new; - struct arm_request_response *arm_req_resp = NULL; - - if (((ext_tcode & 0xFF) == EXTCODE_FETCH_ADD) || - ((ext_tcode & 0xFF) == EXTCODE_LITTLE_ADD)) { - DBGMSG("arm_lock64 called by node: %X " - "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X %8.8X ", - nodeid, (u16) ((addr >> 32) & 0xFFFF), - (u32) (addr & 0xFFFFFFFF), - ext_tcode & 0xFF, - (u32) ((be64_to_cpu(data) >> 32) & 0xFFFFFFFF), - (u32) (be64_to_cpu(data) & 0xFFFFFFFF)); - } else { - DBGMSG("arm_lock64 called by node: %X " - "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X %8.8X arg: " - "%8.8X %8.8X ", - nodeid, (u16) ((addr >> 32) & 0xFFFF), - (u32) (addr & 0xFFFFFFFF), - ext_tcode & 0xFF, - (u32) ((be64_to_cpu(data) >> 32) & 0xFFFFFFFF), - (u32) (be64_to_cpu(data) & 0xFFFFFFFF), - (u32) ((be64_to_cpu(arg) >> 32) & 0xFFFFFFFF), - (u32) (be64_to_cpu(arg) & 0xFFFFFFFF)); - } - spin_lock_irqsave(&host_info_lock, irqflags); - hi = find_host_info(host); /* search addressentry in file_info's for host */ - if (hi != NULL) { - list_for_each_entry(fi, &hi->file_info_list, list) { - entry = fi->addr_list.next; - while (entry != &(fi->addr_list)) { - arm_addr = - list_entry(entry, struct arm_addr, - addr_list); - if (((arm_addr->start) <= (addr)) - && ((arm_addr->end) >= - (addr + sizeof(*store)))) { - found = 1; - break; - } - entry = entry->next; - } - if (found) { - break; - } - } - } - rcode = -1; - if (!found) { - printk(KERN_ERR - "raw1394: arm_lock64 FAILED addr_entry not found" - " -> rcode_address_error\n"); - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (RCODE_ADDRESS_ERROR); - } else { - DBGMSG("arm_lock64 addr_entry FOUND"); - } - if (rcode == -1) { - if (arm_addr->access_rights & ARM_LOCK) { - if (!(arm_addr->client_transactions & ARM_LOCK)) { - memcpy(&old, - (arm_addr->addr_space_buffer) + (addr - - (arm_addr-> - start)), - sizeof(old)); - switch (ext_tcode) { - case (EXTCODE_MASK_SWAP): - new = data | (old & ~arg); - break; - case (EXTCODE_COMPARE_SWAP): - if (old == arg) { - new = data; - } else { - new = old; - } - break; - case (EXTCODE_FETCH_ADD): - new = - cpu_to_be64(be64_to_cpu(data) + - be64_to_cpu(old)); - break; - case (EXTCODE_LITTLE_ADD): - new = - cpu_to_le64(le64_to_cpu(data) + - le64_to_cpu(old)); - break; - case (EXTCODE_BOUNDED_ADD): - if (old != arg) { - new = - cpu_to_be64(be64_to_cpu - (data) + - be64_to_cpu - (old)); - } else { - new = old; - } - break; - case (EXTCODE_WRAP_ADD): - if (old != arg) { - new = - cpu_to_be64(be64_to_cpu - (data) + - be64_to_cpu - (old)); - } else { - new = data; - } - break; - default: - printk(KERN_ERR - "raw1394: arm_lock64 FAILED " - "ext_tcode not allowed -> rcode_type_error\n"); - rcode = RCODE_TYPE_ERROR; /* function not allowed */ - break; - } /*switch */ - if (rcode == -1) { - DBGMSG - ("arm_lock64 -> (rcode_complete)"); - rcode = RCODE_COMPLETE; - memcpy(store, &old, sizeof(*store)); - memcpy((arm_addr->addr_space_buffer) + - (addr - (arm_addr->start)), - &new, sizeof(*store)); - } - } - } else { - rcode = RCODE_TYPE_ERROR; /* function not allowed */ - DBGMSG - ("arm_lock64 -> rcode_type_error (access denied)"); - } - } - if (arm_addr->notification_options & ARM_LOCK) { - byte_t *buf1, *buf2; - DBGMSG("arm_lock64 -> entering notification-section"); - req = __alloc_pending_request(GFP_ATOMIC); - if (!req) { - spin_unlock_irqrestore(&host_info_lock, irqflags); - DBGMSG("arm_lock64 -> rcode_conflict_error"); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request may be retried */ - } - size = sizeof(struct arm_request) + sizeof(struct arm_response) + 3 * sizeof(*store) + sizeof(struct arm_request_response); /* maximum */ - req->data = kmalloc(size, GFP_ATOMIC); - if (!(req->data)) { - free_pending_request(req); - spin_unlock_irqrestore(&host_info_lock, irqflags); - DBGMSG("arm_lock64 -> rcode_conflict_error"); - return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected. - The request may be retried */ - } - req->free_data = 1; - arm_req_resp = (struct arm_request_response *)(req->data); - arm_req = (struct arm_request *)((byte_t *) (req->data) + - (sizeof - (struct - arm_request_response))); - arm_resp = - (struct arm_response *)((byte_t *) (arm_req) + - (sizeof(struct arm_request))); - buf1 = (byte_t *) arm_resp + sizeof(struct arm_response); - buf2 = buf1 + 2 * sizeof(*store); - if ((ext_tcode == EXTCODE_FETCH_ADD) || - (ext_tcode == EXTCODE_LITTLE_ADD)) { - arm_req->buffer_length = sizeof(*store); - memcpy(buf1, &data, sizeof(*store)); - - } else { - arm_req->buffer_length = 2 * sizeof(*store); - memcpy(buf1, &arg, sizeof(*store)); - memcpy(buf1 + sizeof(*store), &data, sizeof(*store)); - } - if (rcode == RCODE_COMPLETE) { - arm_resp->buffer_length = sizeof(*store); - memcpy(buf2, &old, sizeof(*store)); - } else { - arm_resp->buffer_length = 0; - } - req->file_info = fi; - req->req.type = RAW1394_REQ_ARM; - req->req.generation = get_hpsb_generation(host); - req->req.misc = ((((sizeof(*store)) << 16) & (0xFFFF0000)) | - (ARM_LOCK & 0xFF)); - req->req.tag = arm_addr->arm_tag; - req->req.recvb = arm_addr->recvb; - req->req.length = size; - arm_req->generation = req->req.generation; - arm_req->extended_transaction_code = ext_tcode; - arm_req->destination_offset = addr; - arm_req->source_nodeid = nodeid; - arm_req->destination_nodeid = host->node_id; - arm_req->tlabel = (flags >> 10) & 0x3f; - arm_req->tcode = (flags >> 4) & 0x0f; - arm_resp->response_code = rcode; - arm_req_resp->request = int2ptr((arm_addr->recvb) + - sizeof(struct - arm_request_response)); - arm_req_resp->response = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request)); - arm_req->buffer = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request) + - sizeof(struct arm_response)); - arm_resp->buffer = - int2ptr((arm_addr->recvb) + - sizeof(struct arm_request_response) + - sizeof(struct arm_request) + - sizeof(struct arm_response) + 2 * sizeof(*store)); - queue_complete_req(req); - } - spin_unlock_irqrestore(&host_info_lock, irqflags); - return (rcode); -} - -static int arm_register(struct file_info *fi, struct pending_request *req) -{ - int retval; - struct arm_addr *addr; - struct host_info *hi; - struct file_info *fi_hlp = NULL; - struct list_head *entry; - struct arm_addr *arm_addr = NULL; - int same_host, another_host; - unsigned long flags; - - DBGMSG("arm_register called " - "addr(Offset): %8.8x %8.8x length: %u " - "rights: %2.2X notify: %2.2X " - "max_blk_len: %4.4X", - (u32) ((req->req.address >> 32) & 0xFFFF), - (u32) (req->req.address & 0xFFFFFFFF), - req->req.length, ((req->req.misc >> 8) & 0xFF), - (req->req.misc & 0xFF), ((req->req.misc >> 16) & 0xFFFF)); - /* check addressrange */ - if ((((req->req.address) & ~(0xFFFFFFFFFFFFULL)) != 0) || - (((req->req.address + req->req.length) & ~(0xFFFFFFFFFFFFULL)) != - 0)) { - req->req.length = 0; - return (-EINVAL); - } - /* addr-list-entry for fileinfo */ - addr = kmalloc(sizeof(*addr), GFP_KERNEL); - if (!addr) { - req->req.length = 0; - return (-ENOMEM); - } - /* allocation of addr_space_buffer */ - addr->addr_space_buffer = vmalloc(req->req.length); - if (!(addr->addr_space_buffer)) { - kfree(addr); - req->req.length = 0; - return (-ENOMEM); - } - /* initialization of addr_space_buffer */ - if ((req->req.sendb) == (unsigned long)NULL) { - /* init: set 0 */ - memset(addr->addr_space_buffer, 0, req->req.length); - } else { - /* init: user -> kernel */ - if (copy_from_user - (addr->addr_space_buffer, int2ptr(req->req.sendb), - req->req.length)) { - vfree(addr->addr_space_buffer); - kfree(addr); - return (-EFAULT); - } - } - INIT_LIST_HEAD(&addr->addr_list); - addr->arm_tag = req->req.tag; - addr->start = req->req.address; - addr->end = req->req.address + req->req.length; - addr->access_rights = (u8) (req->req.misc & 0x0F); - addr->notification_options = (u8) ((req->req.misc >> 4) & 0x0F); - addr->client_transactions = (u8) ((req->req.misc >> 8) & 0x0F); - addr->access_rights |= addr->client_transactions; - addr->notification_options |= addr->client_transactions; - addr->recvb = req->req.recvb; - addr->rec_length = (u16) ((req->req.misc >> 16) & 0xFFFF); - - spin_lock_irqsave(&host_info_lock, flags); - hi = find_host_info(fi->host); - same_host = 0; - another_host = 0; - /* same host with address-entry containing same addressrange ? */ - list_for_each_entry(fi_hlp, &hi->file_info_list, list) { - entry = fi_hlp->addr_list.next; - while (entry != &(fi_hlp->addr_list)) { - arm_addr = - list_entry(entry, struct arm_addr, addr_list); - if ((arm_addr->start == addr->start) - && (arm_addr->end == addr->end)) { - DBGMSG("same host ownes same " - "addressrange -> EALREADY"); - same_host = 1; - break; - } - entry = entry->next; - } - if (same_host) { - break; - } - } - if (same_host) { - /* addressrange occupied by same host */ - spin_unlock_irqrestore(&host_info_lock, flags); - vfree(addr->addr_space_buffer); - kfree(addr); - return (-EALREADY); - } - /* another host with valid address-entry containing same addressrange */ - list_for_each_entry(hi, &host_info_list, list) { - if (hi->host != fi->host) { - list_for_each_entry(fi_hlp, &hi->file_info_list, list) { - entry = fi_hlp->addr_list.next; - while (entry != &(fi_hlp->addr_list)) { - arm_addr = - list_entry(entry, struct arm_addr, - addr_list); - if ((arm_addr->start == addr->start) - && (arm_addr->end == addr->end)) { - DBGMSG - ("another host ownes same " - "addressrange"); - another_host = 1; - break; - } - entry = entry->next; - } - if (another_host) { - break; - } - } - } - } - spin_unlock_irqrestore(&host_info_lock, flags); - - if (another_host) { - DBGMSG("another hosts entry is valid -> SUCCESS"); - if (copy_to_user(int2ptr(req->req.recvb), - &addr->start, sizeof(u64))) { - printk(KERN_ERR "raw1394: arm_register failed " - " address-range-entry is invalid -> EFAULT !!!\n"); - vfree(addr->addr_space_buffer); - kfree(addr); - return (-EFAULT); - } - free_pending_request(req); /* immediate success or fail */ - /* INSERT ENTRY */ - spin_lock_irqsave(&host_info_lock, flags); - list_add_tail(&addr->addr_list, &fi->addr_list); - spin_unlock_irqrestore(&host_info_lock, flags); - return 0; - } - retval = - hpsb_register_addrspace(&raw1394_highlevel, fi->host, &arm_ops, - req->req.address, - req->req.address + req->req.length); - if (retval) { - /* INSERT ENTRY */ - spin_lock_irqsave(&host_info_lock, flags); - list_add_tail(&addr->addr_list, &fi->addr_list); - spin_unlock_irqrestore(&host_info_lock, flags); - } else { - DBGMSG("arm_register failed errno: %d \n", retval); - vfree(addr->addr_space_buffer); - kfree(addr); - return (-EALREADY); - } - free_pending_request(req); /* immediate success or fail */ - return 0; -} - -static int arm_unregister(struct file_info *fi, struct pending_request *req) -{ - int found = 0; - int retval = 0; - struct list_head *entry; - struct arm_addr *addr = NULL; - struct host_info *hi; - struct file_info *fi_hlp = NULL; - struct arm_addr *arm_addr = NULL; - int another_host; - unsigned long flags; - - DBGMSG("arm_Unregister called addr(Offset): " - "%8.8x %8.8x", - (u32) ((req->req.address >> 32) & 0xFFFF), - (u32) (req->req.address & 0xFFFFFFFF)); - spin_lock_irqsave(&host_info_lock, flags); - /* get addr */ - entry = fi->addr_list.next; - while (entry != &(fi->addr_list)) { - addr = list_entry(entry, struct arm_addr, addr_list); - if (addr->start == req->req.address) { - found = 1; - break; - } - entry = entry->next; - } - if (!found) { - DBGMSG("arm_Unregister addr not found"); - spin_unlock_irqrestore(&host_info_lock, flags); - return (-EINVAL); - } - DBGMSG("arm_Unregister addr found"); - another_host = 0; - /* another host with valid address-entry containing - same addressrange */ - list_for_each_entry(hi, &host_info_list, list) { - if (hi->host != fi->host) { - list_for_each_entry(fi_hlp, &hi->file_info_list, list) { - entry = fi_hlp->addr_list.next; - while (entry != &(fi_hlp->addr_list)) { - arm_addr = list_entry(entry, - struct arm_addr, - addr_list); - if (arm_addr->start == addr->start) { - DBGMSG("another host ownes " - "same addressrange"); - another_host = 1; - break; - } - entry = entry->next; - } - if (another_host) { - break; - } - } - } - } - if (another_host) { - DBGMSG("delete entry from list -> success"); - list_del(&addr->addr_list); - spin_unlock_irqrestore(&host_info_lock, flags); - vfree(addr->addr_space_buffer); - kfree(addr); - free_pending_request(req); /* immediate success or fail */ - return 0; - } - retval = - hpsb_unregister_addrspace(&raw1394_highlevel, fi->host, - addr->start); - if (!retval) { - printk(KERN_ERR "raw1394: arm_Unregister failed -> EINVAL\n"); - spin_unlock_irqrestore(&host_info_lock, flags); - return (-EINVAL); - } - DBGMSG("delete entry from list -> success"); - list_del(&addr->addr_list); - spin_unlock_irqrestore(&host_info_lock, flags); - vfree(addr->addr_space_buffer); - kfree(addr); - free_pending_request(req); /* immediate success or fail */ - return 0; -} - -/* Copy data from ARM buffer(s) to user buffer. */ -static int arm_get_buf(struct file_info *fi, struct pending_request *req) -{ - struct arm_addr *arm_addr = NULL; - unsigned long flags; - unsigned long offset; - - struct list_head *entry; - - DBGMSG("arm_get_buf " - "addr(Offset): %04X %08X length: %u", - (u32) ((req->req.address >> 32) & 0xFFFF), - (u32) (req->req.address & 0xFFFFFFFF), (u32) req->req.length); - - spin_lock_irqsave(&host_info_lock, flags); - entry = fi->addr_list.next; - while (entry != &(fi->addr_list)) { - arm_addr = list_entry(entry, struct arm_addr, addr_list); - if ((arm_addr->start <= req->req.address) && - (arm_addr->end > req->req.address)) { - if (req->req.address + req->req.length <= arm_addr->end) { - offset = req->req.address - arm_addr->start; - spin_unlock_irqrestore(&host_info_lock, flags); - - DBGMSG - ("arm_get_buf copy_to_user( %08X, %p, %u )", - (u32) req->req.recvb, - arm_addr->addr_space_buffer + offset, - (u32) req->req.length); - if (copy_to_user - (int2ptr(req->req.recvb), - arm_addr->addr_space_buffer + offset, - req->req.length)) - return (-EFAULT); - - /* We have to free the request, because we - * queue no response, and therefore nobody - * will free it. */ - free_pending_request(req); - return 0; - } else { - DBGMSG("arm_get_buf request exceeded mapping"); - spin_unlock_irqrestore(&host_info_lock, flags); - return (-EINVAL); - } - } - entry = entry->next; - } - spin_unlock_irqrestore(&host_info_lock, flags); - return (-EINVAL); -} - -/* Copy data from user buffer to ARM buffer(s). */ -static int arm_set_buf(struct file_info *fi, struct pending_request *req) -{ - struct arm_addr *arm_addr = NULL; - unsigned long flags; - unsigned long offset; - - struct list_head *entry; - - DBGMSG("arm_set_buf " - "addr(Offset): %04X %08X length: %u", - (u32) ((req->req.address >> 32) & 0xFFFF), - (u32) (req->req.address & 0xFFFFFFFF), (u32) req->req.length); - - spin_lock_irqsave(&host_info_lock, flags); - entry = fi->addr_list.next; - while (entry != &(fi->addr_list)) { - arm_addr = list_entry(entry, struct arm_addr, addr_list); - if ((arm_addr->start <= req->req.address) && - (arm_addr->end > req->req.address)) { - if (req->req.address + req->req.length <= arm_addr->end) { - offset = req->req.address - arm_addr->start; - spin_unlock_irqrestore(&host_info_lock, flags); - - DBGMSG - ("arm_set_buf copy_from_user( %p, %08X, %u )", - arm_addr->addr_space_buffer + offset, - (u32) req->req.sendb, - (u32) req->req.length); - if (copy_from_user - (arm_addr->addr_space_buffer + offset, - int2ptr(req->req.sendb), - req->req.length)) - return (-EFAULT); - - /* We have to free the request, because we - * queue no response, and therefore nobody - * will free it. */ - free_pending_request(req); - return 0; - } else { - DBGMSG("arm_set_buf request exceeded mapping"); - spin_unlock_irqrestore(&host_info_lock, flags); - return (-EINVAL); - } - } - entry = entry->next; - } - spin_unlock_irqrestore(&host_info_lock, flags); - return (-EINVAL); -} - -static int reset_notification(struct file_info *fi, struct pending_request *req) -{ - DBGMSG("reset_notification called - switch %s ", - (req->req.misc == RAW1394_NOTIFY_OFF) ? "OFF" : "ON"); - if ((req->req.misc == RAW1394_NOTIFY_OFF) || - (req->req.misc == RAW1394_NOTIFY_ON)) { - fi->notification = (u8) req->req.misc; - free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ - return 0; - } - /* error EINVAL (22) invalid argument */ - return (-EINVAL); -} - -static int write_phypacket(struct file_info *fi, struct pending_request *req) -{ - struct hpsb_packet *packet = NULL; - int retval = 0; - quadlet_t data; - unsigned long flags; - - data = be32_to_cpu((u32) req->req.sendb); - DBGMSG("write_phypacket called - quadlet 0x%8.8x ", data); - packet = hpsb_make_phypacket(fi->host, data); - if (!packet) - return -ENOMEM; - req->req.length = 0; - req->packet = packet; - hpsb_set_packet_complete_task(packet, - (void (*)(void *))queue_complete_cb, req); - spin_lock_irqsave(&fi->reqlists_lock, flags); - list_add_tail(&req->list, &fi->req_pending); - spin_unlock_irqrestore(&fi->reqlists_lock, flags); - packet->generation = req->req.generation; - retval = hpsb_send_packet(packet); - DBGMSG("write_phypacket send_packet called => retval: %d ", retval); - if (retval < 0) { - req->req.error = RAW1394_ERROR_SEND_ERROR; - req->req.length = 0; - queue_complete_req(req); - } - return 0; -} - -static int get_config_rom(struct file_info *fi, struct pending_request *req) -{ - int ret = 0; - quadlet_t *data = kmalloc(req->req.length, GFP_KERNEL); - int status; - - if (!data) - return -ENOMEM; - - status = - csr1212_read(fi->host->csr.rom, CSR1212_CONFIG_ROM_SPACE_OFFSET, - data, req->req.length); - if (copy_to_user(int2ptr(req->req.recvb), data, req->req.length)) - ret = -EFAULT; - if (copy_to_user - (int2ptr(req->req.tag), &fi->host->csr.rom->cache_head->len, - sizeof(fi->host->csr.rom->cache_head->len))) - ret = -EFAULT; - if (copy_to_user(int2ptr(req->req.address), &fi->host->csr.generation, - sizeof(fi->host->csr.generation))) - ret = -EFAULT; - if (copy_to_user(int2ptr(req->req.sendb), &status, sizeof(status))) - ret = -EFAULT; - kfree(data); - if (ret >= 0) { - free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ - } - return ret; -} - -static int update_config_rom(struct file_info *fi, struct pending_request *req) -{ - int ret = 0; - quadlet_t *data = kmalloc(req->req.length, GFP_KERNEL); - if (!data) - return -ENOMEM; - if (copy_from_user(data, int2ptr(req->req.sendb), req->req.length)) { - ret = -EFAULT; - } else { - int status = hpsb_update_config_rom(fi->host, - data, req->req.length, - (unsigned char)req->req. - misc); - if (copy_to_user - (int2ptr(req->req.recvb), &status, sizeof(status))) - ret = -ENOMEM; - } - kfree(data); - if (ret >= 0) { - free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ - fi->cfgrom_upd = 1; - } - return ret; -} - -static int modify_config_rom(struct file_info *fi, struct pending_request *req) -{ - struct csr1212_keyval *kv; - struct csr1212_csr_rom_cache *cache; - struct csr1212_dentry *dentry; - u32 dr; - int ret = 0; - - if (req->req.misc == ~0) { - if (req->req.length == 0) - return -EINVAL; - - /* Find an unused slot */ - for (dr = 0; - dr < RAW1394_MAX_USER_CSR_DIRS && fi->csr1212_dirs[dr]; - dr++) ; - - if (dr == RAW1394_MAX_USER_CSR_DIRS) - return -ENOMEM; - - fi->csr1212_dirs[dr] = - csr1212_new_directory(CSR1212_KV_ID_VENDOR); - if (!fi->csr1212_dirs[dr]) - return -ENOMEM; - } else { - dr = req->req.misc; - if (!fi->csr1212_dirs[dr]) - return -EINVAL; - - /* Delete old stuff */ - for (dentry = - fi->csr1212_dirs[dr]->value.directory.dentries_head; - dentry; dentry = dentry->next) { - csr1212_detach_keyval_from_directory(fi->host->csr.rom-> - root_kv, - dentry->kv); - } - - if (req->req.length == 0) { - csr1212_release_keyval(fi->csr1212_dirs[dr]); - fi->csr1212_dirs[dr] = NULL; - - hpsb_update_config_rom_image(fi->host); - free_pending_request(req); - return 0; - } - } - - cache = csr1212_rom_cache_malloc(0, req->req.length); - if (!cache) { - csr1212_release_keyval(fi->csr1212_dirs[dr]); - fi->csr1212_dirs[dr] = NULL; - return -ENOMEM; - } - - cache->filled_head = kmalloc(sizeof(*cache->filled_head), GFP_KERNEL); - if (!cache->filled_head) { - csr1212_release_keyval(fi->csr1212_dirs[dr]); - fi->csr1212_dirs[dr] = NULL; - CSR1212_FREE(cache); - return -ENOMEM; - } - cache->filled_tail = cache->filled_head; - - if (copy_from_user(cache->data, int2ptr(req->req.sendb), - req->req.length)) { - csr1212_release_keyval(fi->csr1212_dirs[dr]); - fi->csr1212_dirs[dr] = NULL; - ret = -EFAULT; - } else { - cache->len = req->req.length; - cache->filled_head->offset_start = 0; - cache->filled_head->offset_end = cache->size - 1; - - cache->layout_head = cache->layout_tail = fi->csr1212_dirs[dr]; - - ret = CSR1212_SUCCESS; - /* parse all the items */ - for (kv = cache->layout_head; ret == CSR1212_SUCCESS && kv; - kv = kv->next) { - ret = csr1212_parse_keyval(kv, cache); - } - - /* attach top level items to the root directory */ - for (dentry = - fi->csr1212_dirs[dr]->value.directory.dentries_head; - ret == CSR1212_SUCCESS && dentry; dentry = dentry->next) { - ret = - csr1212_attach_keyval_to_directory(fi->host->csr. - rom->root_kv, - dentry->kv); - } - - if (ret == CSR1212_SUCCESS) { - ret = hpsb_update_config_rom_image(fi->host); - - if (ret >= 0 && copy_to_user(int2ptr(req->req.recvb), - &dr, sizeof(dr))) { - ret = -ENOMEM; - } - } - } - kfree(cache->filled_head); - CSR1212_FREE(cache); - - if (ret >= 0) { - /* we have to free the request, because we queue no response, - * and therefore nobody will free it */ - free_pending_request(req); - return 0; - } else { - for (dentry = - fi->csr1212_dirs[dr]->value.directory.dentries_head; - dentry; dentry = dentry->next) { - csr1212_detach_keyval_from_directory(fi->host->csr.rom-> - root_kv, - dentry->kv); - } - csr1212_release_keyval(fi->csr1212_dirs[dr]); - fi->csr1212_dirs[dr] = NULL; - return ret; - } -} - -static int state_connected(struct file_info *fi, struct pending_request *req) -{ - int node = req->req.address >> 48; - - req->req.error = RAW1394_ERROR_NONE; - - switch (req->req.type) { - - case RAW1394_REQ_ECHO: - queue_complete_req(req); - return 0; - - case RAW1394_REQ_ARM_REGISTER: - return arm_register(fi, req); - - case RAW1394_REQ_ARM_UNREGISTER: - return arm_unregister(fi, req); - - case RAW1394_REQ_ARM_SET_BUF: - return arm_set_buf(fi, req); - - case RAW1394_REQ_ARM_GET_BUF: - return arm_get_buf(fi, req); - - case RAW1394_REQ_RESET_NOTIFY: - return reset_notification(fi, req); - - case RAW1394_REQ_ISO_SEND: - case RAW1394_REQ_ISO_LISTEN: - printk(KERN_DEBUG "raw1394: old iso ABI has been removed\n"); - req->req.error = RAW1394_ERROR_COMPAT; - req->req.misc = RAW1394_KERNELAPI_VERSION; - queue_complete_req(req); - return 0; - - case RAW1394_REQ_FCP_LISTEN: - handle_fcp_listen(fi, req); - return 0; - - case RAW1394_REQ_RESET_BUS: - if (req->req.misc == RAW1394_LONG_RESET) { - DBGMSG("busreset called (type: LONG)"); - hpsb_reset_bus(fi->host, LONG_RESET); - free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ - return 0; - } - if (req->req.misc == RAW1394_SHORT_RESET) { - DBGMSG("busreset called (type: SHORT)"); - hpsb_reset_bus(fi->host, SHORT_RESET); - free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ - return 0; - } - /* error EINVAL (22) invalid argument */ - return (-EINVAL); - case RAW1394_REQ_GET_ROM: - return get_config_rom(fi, req); - - case RAW1394_REQ_UPDATE_ROM: - return update_config_rom(fi, req); - - case RAW1394_REQ_MODIFY_ROM: - return modify_config_rom(fi, req); - } - - if (req->req.generation != get_hpsb_generation(fi->host)) { - req->req.error = RAW1394_ERROR_GENERATION; - req->req.generation = get_hpsb_generation(fi->host); - req->req.length = 0; - queue_complete_req(req); - return 0; - } - - switch (req->req.type) { - case RAW1394_REQ_PHYPACKET: - return write_phypacket(fi, req); - case RAW1394_REQ_ASYNC_SEND: - return handle_async_send(fi, req); - } - - if (req->req.length == 0) { - req->req.error = RAW1394_ERROR_INVALID_ARG; - queue_complete_req(req); - return 0; - } - - return handle_async_request(fi, req, node); -} - -static ssize_t raw1394_write(struct file *file, const char __user * buffer, - size_t count, loff_t * offset_is_ignored) -{ - struct file_info *fi = file->private_data; - struct pending_request *req; - ssize_t retval = -EBADFD; - -#ifdef CONFIG_COMPAT - if (count == sizeof(struct compat_raw1394_req) && - sizeof(struct compat_raw1394_req) != - sizeof(struct raw1394_request)) { - buffer = raw1394_compat_write(buffer); - if (IS_ERR((__force void *)buffer)) - return PTR_ERR((__force void *)buffer); - } else -#endif - if (count != sizeof(struct raw1394_request)) { - return -EINVAL; - } - - req = alloc_pending_request(); - if (req == NULL) { - return -ENOMEM; - } - req->file_info = fi; - - if (copy_from_user(&req->req, buffer, sizeof(struct raw1394_request))) { - free_pending_request(req); - return -EFAULT; - } - - if (!mutex_trylock(&fi->state_mutex)) { - free_pending_request(req); - return -EAGAIN; - } - - switch (fi->state) { - case opened: - retval = state_opened(fi, req); - break; - - case initialized: - retval = state_initialized(fi, req); - break; - - case connected: - retval = state_connected(fi, req); - break; - } - - mutex_unlock(&fi->state_mutex); - - if (retval < 0) { - free_pending_request(req); - } else { - BUG_ON(retval); - retval = count; - } - - return retval; -} - -/* rawiso operations */ - -/* check if any RAW1394_REQ_RAWISO_ACTIVITY event is already in the - * completion queue (reqlists_lock must be taken) */ -static inline int __rawiso_event_in_queue(struct file_info *fi) -{ - struct pending_request *req; - - list_for_each_entry(req, &fi->req_complete, list) - if (req->req.type == RAW1394_REQ_RAWISO_ACTIVITY) - return 1; - - return 0; -} - -/* put a RAWISO_ACTIVITY event in the queue, if one isn't there already */ -static void queue_rawiso_event(struct file_info *fi) -{ - unsigned long flags; - - spin_lock_irqsave(&fi->reqlists_lock, flags); - - /* only one ISO activity event may be in the queue */ - if (!__rawiso_event_in_queue(fi)) { - struct pending_request *req = - __alloc_pending_request(GFP_ATOMIC); - - if (req) { - req->file_info = fi; - req->req.type = RAW1394_REQ_RAWISO_ACTIVITY; - req->req.generation = get_hpsb_generation(fi->host); - __queue_complete_req(req); - } else { - /* on allocation failure, signal an overflow */ - if (fi->iso_handle) { - atomic_inc(&fi->iso_handle->overflows); - } - } - } - spin_unlock_irqrestore(&fi->reqlists_lock, flags); -} - -static void rawiso_activity_cb(struct hpsb_iso *iso) -{ - unsigned long flags; - struct host_info *hi; - struct file_info *fi; - - spin_lock_irqsave(&host_info_lock, flags); - hi = find_host_info(iso->host); - - if (hi != NULL) { - list_for_each_entry(fi, &hi->file_info_list, list) { - if (fi->iso_handle == iso) - queue_rawiso_event(fi); - } - } - - spin_unlock_irqrestore(&host_info_lock, flags); -} - -/* helper function - gather all the kernel iso status bits for returning to user-space */ -static void raw1394_iso_fill_status(struct hpsb_iso *iso, - struct raw1394_iso_status *stat) -{ - int overflows = atomic_read(&iso->overflows); - int skips = atomic_read(&iso->skips); - - stat->config.data_buf_size = iso->buf_size; - stat->config.buf_packets = iso->buf_packets; - stat->config.channel = iso->channel; - stat->config.speed = iso->speed; - stat->config.irq_interval = iso->irq_interval; - stat->n_packets = hpsb_iso_n_ready(iso); - stat->overflows = ((skips & 0xFFFF) << 16) | ((overflows & 0xFFFF)); - stat->xmit_cycle = iso->xmit_cycle; -} - -static int raw1394_iso_xmit_init(struct file_info *fi, void __user * uaddr) -{ - struct raw1394_iso_status stat; - - if (!fi->host) - return -EINVAL; - - if (copy_from_user(&stat, uaddr, sizeof(stat))) - return -EFAULT; - - fi->iso_handle = hpsb_iso_xmit_init(fi->host, - stat.config.data_buf_size, - stat.config.buf_packets, - stat.config.channel, - stat.config.speed, - stat.config.irq_interval, - rawiso_activity_cb); - if (!fi->iso_handle) - return -ENOMEM; - - fi->iso_state = RAW1394_ISO_XMIT; - - raw1394_iso_fill_status(fi->iso_handle, &stat); - if (copy_to_user(uaddr, &stat, sizeof(stat))) - return -EFAULT; - - /* queue an event to get things started */ - rawiso_activity_cb(fi->iso_handle); - - return 0; -} - -static int raw1394_iso_recv_init(struct file_info *fi, void __user * uaddr) -{ - struct raw1394_iso_status stat; - - if (!fi->host) - return -EINVAL; - - if (copy_from_user(&stat, uaddr, sizeof(stat))) - return -EFAULT; - - fi->iso_handle = hpsb_iso_recv_init(fi->host, - stat.config.data_buf_size, - stat.config.buf_packets, - stat.config.channel, - stat.config.dma_mode, - stat.config.irq_interval, - rawiso_activity_cb); - if (!fi->iso_handle) - return -ENOMEM; - - fi->iso_state = RAW1394_ISO_RECV; - - raw1394_iso_fill_status(fi->iso_handle, &stat); - if (copy_to_user(uaddr, &stat, sizeof(stat))) - return -EFAULT; - return 0; -} - -static int raw1394_iso_get_status(struct file_info *fi, void __user * uaddr) -{ - struct raw1394_iso_status stat; - struct hpsb_iso *iso = fi->iso_handle; - - raw1394_iso_fill_status(fi->iso_handle, &stat); - if (copy_to_user(uaddr, &stat, sizeof(stat))) - return -EFAULT; - - /* reset overflow counter */ - atomic_set(&iso->overflows, 0); - /* reset skip counter */ - atomic_set(&iso->skips, 0); - - return 0; -} - -/* copy N packet_infos out of the ringbuffer into user-supplied array */ -static int raw1394_iso_recv_packets(struct file_info *fi, void __user * uaddr) -{ - struct raw1394_iso_packets upackets; - unsigned int packet = fi->iso_handle->first_packet; - int i; - - if (copy_from_user(&upackets, uaddr, sizeof(upackets))) - return -EFAULT; - - if (upackets.n_packets > hpsb_iso_n_ready(fi->iso_handle)) - return -EINVAL; - - /* ensure user-supplied buffer is accessible and big enough */ - if (!access_ok(VERIFY_WRITE, upackets.infos, - upackets.n_packets * - sizeof(struct raw1394_iso_packet_info))) - return -EFAULT; - - /* copy the packet_infos out */ - for (i = 0; i < upackets.n_packets; i++) { - if (__copy_to_user(&upackets.infos[i], - &fi->iso_handle->infos[packet], - sizeof(struct raw1394_iso_packet_info))) - return -EFAULT; - - packet = (packet + 1) % fi->iso_handle->buf_packets; - } - - return 0; -} - -/* copy N packet_infos from user to ringbuffer, and queue them for transmission */ -static int raw1394_iso_send_packets(struct file_info *fi, void __user * uaddr) -{ - struct raw1394_iso_packets upackets; - int i, rv; - - if (copy_from_user(&upackets, uaddr, sizeof(upackets))) - return -EFAULT; - - if (upackets.n_packets >= fi->iso_handle->buf_packets) - return -EINVAL; - - if (upackets.n_packets >= hpsb_iso_n_ready(fi->iso_handle)) - return -EAGAIN; - - /* ensure user-supplied buffer is accessible and big enough */ - if (!access_ok(VERIFY_READ, upackets.infos, - upackets.n_packets * - sizeof(struct raw1394_iso_packet_info))) - return -EFAULT; - - /* copy the infos structs in and queue the packets */ - for (i = 0; i < upackets.n_packets; i++) { - struct raw1394_iso_packet_info info; - - if (__copy_from_user(&info, &upackets.infos[i], - sizeof(struct raw1394_iso_packet_info))) - return -EFAULT; - - rv = hpsb_iso_xmit_queue_packet(fi->iso_handle, info.offset, - info.len, info.tag, info.sy); - if (rv) - return rv; - } - - return 0; -} - -static void raw1394_iso_shutdown(struct file_info *fi) -{ - if (fi->iso_handle) - hpsb_iso_shutdown(fi->iso_handle); - - fi->iso_handle = NULL; - fi->iso_state = RAW1394_ISO_INACTIVE; -} - -static int raw1394_read_cycle_timer(struct file_info *fi, void __user * uaddr) -{ - struct raw1394_cycle_timer ct; - int err; - - err = hpsb_read_cycle_timer(fi->host, &ct.cycle_timer, &ct.local_time); - if (!err) - if (copy_to_user(uaddr, &ct, sizeof(ct))) - err = -EFAULT; - return err; -} - -/* mmap the rawiso xmit/recv buffer */ -static int raw1394_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct file_info *fi = file->private_data; - int ret; - - if (!mutex_trylock(&fi->state_mutex)) - return -EAGAIN; - - if (fi->iso_state == RAW1394_ISO_INACTIVE) - ret = -EINVAL; - else - ret = dma_region_mmap(&fi->iso_handle->data_buf, file, vma); - - mutex_unlock(&fi->state_mutex); - - return ret; -} - -static long raw1394_ioctl_inactive(struct file_info *fi, unsigned int cmd, - void __user *argp) -{ - switch (cmd) { - case RAW1394_IOC_ISO_XMIT_INIT: - return raw1394_iso_xmit_init(fi, argp); - case RAW1394_IOC_ISO_RECV_INIT: - return raw1394_iso_recv_init(fi, argp); - default: - return -EINVAL; - } -} - -static long raw1394_ioctl_recv(struct file_info *fi, unsigned int cmd, - unsigned long arg) -{ - void __user *argp = (void __user *)arg; - - switch (cmd) { - case RAW1394_IOC_ISO_RECV_START:{ - int args[3]; - - if (copy_from_user(&args[0], argp, sizeof(args))) - return -EFAULT; - return hpsb_iso_recv_start(fi->iso_handle, - args[0], args[1], args[2]); - } - case RAW1394_IOC_ISO_XMIT_RECV_STOP: - hpsb_iso_stop(fi->iso_handle); - return 0; - case RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL: - return hpsb_iso_recv_listen_channel(fi->iso_handle, arg); - case RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL: - return hpsb_iso_recv_unlisten_channel(fi->iso_handle, arg); - case RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK:{ - u64 mask; - - if (copy_from_user(&mask, argp, sizeof(mask))) - return -EFAULT; - return hpsb_iso_recv_set_channel_mask(fi->iso_handle, - mask); - } - case RAW1394_IOC_ISO_GET_STATUS: - return raw1394_iso_get_status(fi, argp); - case RAW1394_IOC_ISO_RECV_PACKETS: - return raw1394_iso_recv_packets(fi, argp); - case RAW1394_IOC_ISO_RECV_RELEASE_PACKETS: - return hpsb_iso_recv_release_packets(fi->iso_handle, arg); - case RAW1394_IOC_ISO_RECV_FLUSH: - return hpsb_iso_recv_flush(fi->iso_handle); - case RAW1394_IOC_ISO_SHUTDOWN: - raw1394_iso_shutdown(fi); - return 0; - case RAW1394_IOC_ISO_QUEUE_ACTIVITY: - queue_rawiso_event(fi); - return 0; - default: - return -EINVAL; - } -} - -static long raw1394_ioctl_xmit(struct file_info *fi, unsigned int cmd, - void __user *argp) -{ - switch (cmd) { - case RAW1394_IOC_ISO_XMIT_START:{ - int args[2]; - - if (copy_from_user(&args[0], argp, sizeof(args))) - return -EFAULT; - return hpsb_iso_xmit_start(fi->iso_handle, - args[0], args[1]); - } - case RAW1394_IOC_ISO_XMIT_SYNC: - return hpsb_iso_xmit_sync(fi->iso_handle); - case RAW1394_IOC_ISO_XMIT_RECV_STOP: - hpsb_iso_stop(fi->iso_handle); - return 0; - case RAW1394_IOC_ISO_GET_STATUS: - return raw1394_iso_get_status(fi, argp); - case RAW1394_IOC_ISO_XMIT_PACKETS: - return raw1394_iso_send_packets(fi, argp); - case RAW1394_IOC_ISO_SHUTDOWN: - raw1394_iso_shutdown(fi); - return 0; - case RAW1394_IOC_ISO_QUEUE_ACTIVITY: - queue_rawiso_event(fi); - return 0; - default: - return -EINVAL; - } -} - -/* ioctl is only used for rawiso operations */ -static long raw1394_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct file_info *fi = file->private_data; - void __user *argp = (void __user *)arg; - long ret; - - /* state-independent commands */ - switch(cmd) { - case RAW1394_IOC_GET_CYCLE_TIMER: - return raw1394_read_cycle_timer(fi, argp); - default: - break; - } - - if (!mutex_trylock(&fi->state_mutex)) - return -EAGAIN; - - switch (fi->iso_state) { - case RAW1394_ISO_INACTIVE: - ret = raw1394_ioctl_inactive(fi, cmd, argp); - break; - case RAW1394_ISO_RECV: - ret = raw1394_ioctl_recv(fi, cmd, arg); - break; - case RAW1394_ISO_XMIT: - ret = raw1394_ioctl_xmit(fi, cmd, argp); - break; - default: - ret = -EINVAL; - break; - } - - mutex_unlock(&fi->state_mutex); - - return ret; -} - -#ifdef CONFIG_COMPAT -struct raw1394_iso_packets32 { - __u32 n_packets; - compat_uptr_t infos; -} __attribute__((packed)); - -struct raw1394_cycle_timer32 { - __u32 cycle_timer; - __u64 local_time; -} -#if defined(CONFIG_X86_64) || defined(CONFIG_IA64) -__attribute__((packed)) -#endif -; - -#define RAW1394_IOC_ISO_RECV_PACKETS32 \ - _IOW ('#', 0x25, struct raw1394_iso_packets32) -#define RAW1394_IOC_ISO_XMIT_PACKETS32 \ - _IOW ('#', 0x27, struct raw1394_iso_packets32) -#define RAW1394_IOC_GET_CYCLE_TIMER32 \ - _IOR ('#', 0x30, struct raw1394_cycle_timer32) - -static long raw1394_iso_xmit_recv_packets32(struct file *file, unsigned int cmd, - struct raw1394_iso_packets32 __user *arg) -{ - compat_uptr_t infos32; - void __user *infos; - long err = -EFAULT; - struct raw1394_iso_packets __user *dst = compat_alloc_user_space(sizeof(struct raw1394_iso_packets)); - - if (!copy_in_user(&dst->n_packets, &arg->n_packets, sizeof arg->n_packets) && - !copy_from_user(&infos32, &arg->infos, sizeof infos32)) { - infos = compat_ptr(infos32); - if (!copy_to_user(&dst->infos, &infos, sizeof infos)) - err = raw1394_ioctl(file, cmd, (unsigned long)dst); - } - return err; -} - -static long raw1394_read_cycle_timer32(struct file_info *fi, void __user * uaddr) -{ - struct raw1394_cycle_timer32 ct; - int err; - - err = hpsb_read_cycle_timer(fi->host, &ct.cycle_timer, &ct.local_time); - if (!err) - if (copy_to_user(uaddr, &ct, sizeof(ct))) - err = -EFAULT; - return err; -} - -static long raw1394_compat_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct file_info *fi = file->private_data; - void __user *argp = (void __user *)arg; - long err; - - switch (cmd) { - /* These requests have same format as long as 'int' has same size. */ - case RAW1394_IOC_ISO_RECV_INIT: - case RAW1394_IOC_ISO_RECV_START: - case RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL: - case RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL: - case RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK: - case RAW1394_IOC_ISO_RECV_RELEASE_PACKETS: - case RAW1394_IOC_ISO_RECV_FLUSH: - case RAW1394_IOC_ISO_XMIT_RECV_STOP: - case RAW1394_IOC_ISO_XMIT_INIT: - case RAW1394_IOC_ISO_XMIT_START: - case RAW1394_IOC_ISO_XMIT_SYNC: - case RAW1394_IOC_ISO_GET_STATUS: - case RAW1394_IOC_ISO_SHUTDOWN: - case RAW1394_IOC_ISO_QUEUE_ACTIVITY: - err = raw1394_ioctl(file, cmd, arg); - break; - /* These request have different format. */ - case RAW1394_IOC_ISO_RECV_PACKETS32: - err = raw1394_iso_xmit_recv_packets32(file, RAW1394_IOC_ISO_RECV_PACKETS, argp); - break; - case RAW1394_IOC_ISO_XMIT_PACKETS32: - err = raw1394_iso_xmit_recv_packets32(file, RAW1394_IOC_ISO_XMIT_PACKETS, argp); - break; - case RAW1394_IOC_GET_CYCLE_TIMER32: - err = raw1394_read_cycle_timer32(fi, argp); - break; - default: - err = -EINVAL; - break; - } - - return err; -} -#endif - -static unsigned int raw1394_poll(struct file *file, poll_table * pt) -{ - struct file_info *fi = file->private_data; - unsigned int mask = POLLOUT | POLLWRNORM; - unsigned long flags; - - poll_wait(file, &fi->wait_complete, pt); - - spin_lock_irqsave(&fi->reqlists_lock, flags); - if (!list_empty(&fi->req_complete)) { - mask |= POLLIN | POLLRDNORM; - } - spin_unlock_irqrestore(&fi->reqlists_lock, flags); - - return mask; -} - -static int raw1394_open(struct inode *inode, struct file *file) -{ - struct file_info *fi; - - fi = kzalloc(sizeof(*fi), GFP_KERNEL); - if (!fi) - return -ENOMEM; - - fi->notification = (u8) RAW1394_NOTIFY_ON; /* busreset notification */ - - INIT_LIST_HEAD(&fi->list); - mutex_init(&fi->state_mutex); - fi->state = opened; - INIT_LIST_HEAD(&fi->req_pending); - INIT_LIST_HEAD(&fi->req_complete); - spin_lock_init(&fi->reqlists_lock); - init_waitqueue_head(&fi->wait_complete); - INIT_LIST_HEAD(&fi->addr_list); - - file->private_data = fi; - - return nonseekable_open(inode, file); -} - -static int raw1394_release(struct inode *inode, struct file *file) -{ - struct file_info *fi = file->private_data; - struct list_head *lh; - struct pending_request *req; - int i, fail; - int retval = 0; - struct list_head *entry; - struct arm_addr *addr = NULL; - struct host_info *hi; - struct file_info *fi_hlp = NULL; - struct arm_addr *arm_addr = NULL; - int another_host; - int csr_mod = 0; - unsigned long flags; - - if (fi->iso_state != RAW1394_ISO_INACTIVE) - raw1394_iso_shutdown(fi); - - spin_lock_irqsave(&host_info_lock, flags); - - fail = 0; - /* set address-entries invalid */ - - while (!list_empty(&fi->addr_list)) { - another_host = 0; - lh = fi->addr_list.next; - addr = list_entry(lh, struct arm_addr, addr_list); - /* another host with valid address-entry containing - same addressrange? */ - list_for_each_entry(hi, &host_info_list, list) { - if (hi->host != fi->host) { - list_for_each_entry(fi_hlp, &hi->file_info_list, - list) { - entry = fi_hlp->addr_list.next; - while (entry != &(fi_hlp->addr_list)) { - arm_addr = list_entry(entry, struct - arm_addr, - addr_list); - if (arm_addr->start == - addr->start) { - DBGMSG - ("raw1394_release: " - "another host ownes " - "same addressrange"); - another_host = 1; - break; - } - entry = entry->next; - } - if (another_host) { - break; - } - } - } - } - if (!another_host) { - DBGMSG("raw1394_release: call hpsb_arm_unregister"); - retval = - hpsb_unregister_addrspace(&raw1394_highlevel, - fi->host, addr->start); - if (!retval) { - ++fail; - printk(KERN_ERR - "raw1394_release arm_Unregister failed\n"); - } - } - DBGMSG("raw1394_release: delete addr_entry from list"); - list_del(&addr->addr_list); - vfree(addr->addr_space_buffer); - kfree(addr); - } /* while */ - spin_unlock_irqrestore(&host_info_lock, flags); - if (fail > 0) { - printk(KERN_ERR "raw1394: during addr_list-release " - "error(s) occurred \n"); - } - - for (;;) { - /* This locked section guarantees that neither - * complete nor pending requests exist once i!=0 */ - spin_lock_irqsave(&fi->reqlists_lock, flags); - while ((req = __next_complete_req(fi))) - free_pending_request(req); - - i = list_empty(&fi->req_pending); - spin_unlock_irqrestore(&fi->reqlists_lock, flags); - - if (i) - break; - /* - * Sleep until more requests can be freed. - * - * NB: We call the macro wait_event() with a condition argument - * with side effect. This is only possible because the side - * effect does not occur until the condition became true, and - * wait_event() won't evaluate the condition again after that. - */ - wait_event(fi->wait_complete, (req = next_complete_req(fi))); - free_pending_request(req); - } - - /* Remove any sub-trees left by user space programs */ - for (i = 0; i < RAW1394_MAX_USER_CSR_DIRS; i++) { - struct csr1212_dentry *dentry; - if (!fi->csr1212_dirs[i]) - continue; - for (dentry = - fi->csr1212_dirs[i]->value.directory.dentries_head; dentry; - dentry = dentry->next) { - csr1212_detach_keyval_from_directory(fi->host->csr.rom-> - root_kv, - dentry->kv); - } - csr1212_release_keyval(fi->csr1212_dirs[i]); - fi->csr1212_dirs[i] = NULL; - csr_mod = 1; - } - - if ((csr_mod || fi->cfgrom_upd) - && hpsb_update_config_rom_image(fi->host) < 0) - HPSB_ERR - ("Failed to generate Configuration ROM image for host %d", - fi->host->id); - - if (fi->state == connected) { - spin_lock_irqsave(&host_info_lock, flags); - list_del(&fi->list); - spin_unlock_irqrestore(&host_info_lock, flags); - - put_device(&fi->host->device); - } - - spin_lock_irqsave(&host_info_lock, flags); - if (fi->host) - module_put(fi->host->driver->owner); - spin_unlock_irqrestore(&host_info_lock, flags); - - kfree(fi); - - return 0; -} - -/*** HOTPLUG STUFF **********************************************************/ -/* - * Export information about protocols/devices supported by this driver. - */ -#ifdef MODULE -static const struct ieee1394_device_id raw1394_id_table[] = { - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = AVC_SW_VERSION_ENTRY & 0xffffff}, - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = CAMERA_SW_VERSION_ENTRY & 0xffffff}, - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = (CAMERA_SW_VERSION_ENTRY + 1) & 0xffffff}, - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = (CAMERA_SW_VERSION_ENTRY + 2) & 0xffffff}, - {} -}; - -MODULE_DEVICE_TABLE(ieee1394, raw1394_id_table); -#endif /* MODULE */ - -static struct hpsb_protocol_driver raw1394_driver = { - .name = "raw1394", -}; - -/******************************************************************************/ - -static struct hpsb_highlevel raw1394_highlevel = { - .name = RAW1394_DEVICE_NAME, - .add_host = add_host, - .remove_host = remove_host, - .host_reset = host_reset, - .fcp_request = fcp_request, -}; - -static struct cdev raw1394_cdev; -static const struct file_operations raw1394_fops = { - .owner = THIS_MODULE, - .read = raw1394_read, - .write = raw1394_write, - .mmap = raw1394_mmap, - .unlocked_ioctl = raw1394_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = raw1394_compat_ioctl, -#endif - .poll = raw1394_poll, - .open = raw1394_open, - .release = raw1394_release, - .llseek = no_llseek, -}; - -static int __init init_raw1394(void) -{ - int ret = 0; - - hpsb_register_highlevel(&raw1394_highlevel); - - if (IS_ERR - (device_create(hpsb_protocol_class, NULL, - MKDEV(IEEE1394_MAJOR, - IEEE1394_MINOR_BLOCK_RAW1394 * 16), - NULL, RAW1394_DEVICE_NAME))) { - ret = -EFAULT; - goto out_unreg; - } - - cdev_init(&raw1394_cdev, &raw1394_fops); - raw1394_cdev.owner = THIS_MODULE; - ret = cdev_add(&raw1394_cdev, IEEE1394_RAW1394_DEV, 1); - if (ret) { - HPSB_ERR("raw1394 failed to register minor device block"); - goto out_dev; - } - - HPSB_INFO("raw1394: /dev/%s device initialized", RAW1394_DEVICE_NAME); - - ret = hpsb_register_protocol(&raw1394_driver); - if (ret) { - HPSB_ERR("raw1394: failed to register protocol"); - cdev_del(&raw1394_cdev); - goto out_dev; - } - - goto out; - - out_dev: - device_destroy(hpsb_protocol_class, - MKDEV(IEEE1394_MAJOR, - IEEE1394_MINOR_BLOCK_RAW1394 * 16)); - out_unreg: - hpsb_unregister_highlevel(&raw1394_highlevel); - out: - return ret; -} - -static void __exit cleanup_raw1394(void) -{ - device_destroy(hpsb_protocol_class, - MKDEV(IEEE1394_MAJOR, - IEEE1394_MINOR_BLOCK_RAW1394 * 16)); - cdev_del(&raw1394_cdev); - hpsb_unregister_highlevel(&raw1394_highlevel); - hpsb_unregister_protocol(&raw1394_driver); -} - -module_init(init_raw1394); -module_exit(cleanup_raw1394); -MODULE_LICENSE("GPL"); diff --git a/drivers/ieee1394/raw1394.h b/drivers/ieee1394/raw1394.h deleted file mode 100644 index 963ac20373d2..000000000000 --- a/drivers/ieee1394/raw1394.h +++ /dev/null @@ -1,191 +0,0 @@ -#ifndef IEEE1394_RAW1394_H -#define IEEE1394_RAW1394_H - -/* header for the raw1394 API that is exported to user-space */ - -#define RAW1394_KERNELAPI_VERSION 4 - -/* state: opened */ -#define RAW1394_REQ_INITIALIZE 1 - -/* state: initialized */ -#define RAW1394_REQ_LIST_CARDS 2 -#define RAW1394_REQ_SET_CARD 3 - -/* state: connected */ -#define RAW1394_REQ_ASYNC_READ 100 -#define RAW1394_REQ_ASYNC_WRITE 101 -#define RAW1394_REQ_LOCK 102 -#define RAW1394_REQ_LOCK64 103 -#define RAW1394_REQ_ISO_SEND 104 /* removed ABI, now a no-op */ -#define RAW1394_REQ_ASYNC_SEND 105 -#define RAW1394_REQ_ASYNC_STREAM 106 - -#define RAW1394_REQ_ISO_LISTEN 200 /* removed ABI, now a no-op */ -#define RAW1394_REQ_FCP_LISTEN 201 -#define RAW1394_REQ_RESET_BUS 202 -#define RAW1394_REQ_GET_ROM 203 -#define RAW1394_REQ_UPDATE_ROM 204 -#define RAW1394_REQ_ECHO 205 -#define RAW1394_REQ_MODIFY_ROM 206 - -#define RAW1394_REQ_ARM_REGISTER 300 -#define RAW1394_REQ_ARM_UNREGISTER 301 -#define RAW1394_REQ_ARM_SET_BUF 302 -#define RAW1394_REQ_ARM_GET_BUF 303 - -#define RAW1394_REQ_RESET_NOTIFY 400 - -#define RAW1394_REQ_PHYPACKET 500 - -/* kernel to user */ -#define RAW1394_REQ_BUS_RESET 10000 -#define RAW1394_REQ_ISO_RECEIVE 10001 -#define RAW1394_REQ_FCP_REQUEST 10002 -#define RAW1394_REQ_ARM 10003 -#define RAW1394_REQ_RAWISO_ACTIVITY 10004 - -/* error codes */ -#define RAW1394_ERROR_NONE 0 -#define RAW1394_ERROR_COMPAT (-1001) -#define RAW1394_ERROR_STATE_ORDER (-1002) -#define RAW1394_ERROR_GENERATION (-1003) -#define RAW1394_ERROR_INVALID_ARG (-1004) -#define RAW1394_ERROR_MEMFAULT (-1005) -#define RAW1394_ERROR_ALREADY (-1006) - -#define RAW1394_ERROR_EXCESSIVE (-1020) -#define RAW1394_ERROR_UNTIDY_LEN (-1021) - -#define RAW1394_ERROR_SEND_ERROR (-1100) -#define RAW1394_ERROR_ABORTED (-1101) -#define RAW1394_ERROR_TIMEOUT (-1102) - -/* arm_codes */ -#define ARM_READ 1 -#define ARM_WRITE 2 -#define ARM_LOCK 4 - -#define RAW1394_LONG_RESET 0 -#define RAW1394_SHORT_RESET 1 - -/* busresetnotify ... */ -#define RAW1394_NOTIFY_OFF 0 -#define RAW1394_NOTIFY_ON 1 - -#include - -struct raw1394_request { - __u32 type; - __s32 error; - __u32 misc; - - __u32 generation; - __u32 length; - - __u64 address; - - __u64 tag; - - __u64 sendb; - __u64 recvb; -}; - -struct raw1394_khost_list { - __u32 nodes; - __u8 name[32]; -}; - -typedef struct arm_request { - __u16 destination_nodeid; - __u16 source_nodeid; - __u64 destination_offset; - __u8 tlabel; - __u8 tcode; - __u8 extended_transaction_code; - __u32 generation; - __u16 buffer_length; - __u8 __user *buffer; -} *arm_request_t; - -typedef struct arm_response { - __s32 response_code; - __u16 buffer_length; - __u8 __user *buffer; -} *arm_response_t; - -typedef struct arm_request_response { - struct arm_request __user *request; - struct arm_response __user *response; -} *arm_request_response_t; - -/* rawiso API */ -#include "ieee1394-ioctl.h" - -/* per-packet metadata embedded in the ringbuffer */ -/* must be identical to hpsb_iso_packet_info in iso.h! */ -struct raw1394_iso_packet_info { - __u32 offset; - __u16 len; - __u16 cycle; /* recv only */ - __u8 channel; /* recv only */ - __u8 tag; - __u8 sy; -}; - -/* argument for RAW1394_ISO_RECV/XMIT_PACKETS ioctls */ -struct raw1394_iso_packets { - __u32 n_packets; - struct raw1394_iso_packet_info __user *infos; -}; - -struct raw1394_iso_config { - /* size of packet data buffer, in bytes (will be rounded up to PAGE_SIZE) */ - __u32 data_buf_size; - - /* # of packets to buffer */ - __u32 buf_packets; - - /* iso channel (set to -1 for multi-channel recv) */ - __s32 channel; - - /* xmit only - iso transmission speed */ - __u8 speed; - - /* The mode of the dma when receiving iso data. Must be supported by chip */ - __u8 dma_mode; - - /* max. latency of buffer, in packets (-1 if you don't care) */ - __s32 irq_interval; -}; - -/* argument to RAW1394_ISO_XMIT/RECV_INIT and RAW1394_ISO_GET_STATUS */ -struct raw1394_iso_status { - /* current settings */ - struct raw1394_iso_config config; - - /* number of packets waiting to be filled with data (ISO transmission) - or containing data received (ISO reception) */ - __u32 n_packets; - - /* approximate number of packets dropped due to overflow or - underflow of the packet buffer (a value of zero guarantees - that no packets have been dropped) */ - __u32 overflows; - - /* cycle number at which next packet will be transmitted; - -1 if not known */ - __s16 xmit_cycle; -}; - -/* argument to RAW1394_IOC_GET_CYCLE_TIMER ioctl */ -struct raw1394_cycle_timer { - /* contents of Isochronous Cycle Timer register, - as in OHCI 1.1 clause 5.13 (also with non-OHCI hosts) */ - __u32 cycle_timer; - - /* local time in microseconds since Epoch, - simultaneously read with cycle timer */ - __u64 local_time; -}; -#endif /* IEEE1394_RAW1394_H */ diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c deleted file mode 100644 index d6e251a300ce..000000000000 --- a/drivers/ieee1394/sbp2.c +++ /dev/null @@ -1,2138 +0,0 @@ -/* - * sbp2.c - SBP-2 protocol driver for IEEE-1394 - * - * Copyright (C) 2000 James Goodwin, Filanet Corporation (www.filanet.com) - * jamesg@filanet.com (JSG) - * - * Copyright (C) 2003 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - * Brief Description: - * - * This driver implements the Serial Bus Protocol 2 (SBP-2) over IEEE-1394 - * under Linux. The SBP-2 driver is implemented as an IEEE-1394 high-level - * driver. It also registers as a SCSI lower-level driver in order to accept - * SCSI commands for transport using SBP-2. - * - * You may access any attached SBP-2 (usually storage devices) as regular - * SCSI devices. E.g. mount /dev/sda1, fdisk, mkfs, etc.. - * - * See http://www.t10.org/drafts.htm#sbp2 for the final draft of the SBP-2 - * specification and for where to purchase the official standard. - * - * TODO: - * - look into possible improvements of the SCSI error handlers - * - handle Unit_Characteristics.mgt_ORB_timeout and .ORB_size - * - handle Logical_Unit_Number.ordered - * - handle src == 1 in status blocks - * - reimplement the DMA mapping in absence of physical DMA so that - * bus_to_virt is no longer required - * - debug the handling of absent physical DMA - * - replace CONFIG_IEEE1394_SBP2_PHYS_DMA by automatic detection - * (this is easy but depends on the previous two TODO items) - * - make the parameter serialize_io configurable per device - * - move all requests to fetch agent registers into non-atomic context, - * replace all usages of sbp2util_node_write_no_wait by true transactions - * Grep for inline FIXME comments below. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA -#include /* for bus_to_virt */ -#endif - -#include -#include -#include -#include -#include - -#include "csr1212.h" -#include "highlevel.h" -#include "hosts.h" -#include "ieee1394.h" -#include "ieee1394_core.h" -#include "ieee1394_hotplug.h" -#include "ieee1394_transactions.h" -#include "ieee1394_types.h" -#include "nodemgr.h" -#include "sbp2.h" - -/* - * Module load parameter definitions - */ - -/* - * Change max_speed on module load if you have a bad IEEE-1394 - * controller that has trouble running 2KB packets at 400mb. - * - * NOTE: On certain OHCI parts I have seen short packets on async transmit - * (probably due to PCI latency/throughput issues with the part). You can - * bump down the speed if you are running into problems. - */ -static int sbp2_max_speed = IEEE1394_SPEED_MAX; -module_param_named(max_speed, sbp2_max_speed, int, 0644); -MODULE_PARM_DESC(max_speed, "Limit data transfer speed (5 <= 3200, " - "4 <= 1600, 3 <= 800, 2 <= 400, 1 <= 200, 0 = 100 Mb/s)"); - -/* - * Set serialize_io to 0 or N to use dynamically appended lists of command ORBs. - * This is and always has been buggy in multiple subtle ways. See above TODOs. - */ -static int sbp2_serialize_io = 1; -module_param_named(serialize_io, sbp2_serialize_io, bool, 0444); -MODULE_PARM_DESC(serialize_io, "Serialize requests coming from SCSI drivers " - "(default = Y, faster but buggy = N)"); - -/* - * Adjust max_sectors if you'd like to influence how many sectors each SCSI - * command can transfer at most. Please note that some older SBP-2 bridge - * chips are broken for transfers greater or equal to 128KB, therefore - * max_sectors used to be a safe 255 sectors for many years. We now have a - * default of 0 here which means that we let the SCSI stack choose a limit. - * - * The SBP2_WORKAROUND_128K_MAX_TRANS flag, if set either in the workarounds - * module parameter or in the sbp2_workarounds_table[], will override the - * value of max_sectors. We should use sbp2_workarounds_table[] to cover any - * bridge chip which becomes known to need the 255 sectors limit. - */ -static int sbp2_max_sectors; -module_param_named(max_sectors, sbp2_max_sectors, int, 0444); -MODULE_PARM_DESC(max_sectors, "Change max sectors per I/O supported " - "(default = 0 = use SCSI stack's default)"); - -/* - * Exclusive login to sbp2 device? In most cases, the sbp2 driver should - * do an exclusive login, as it's generally unsafe to have two hosts - * talking to a single sbp2 device at the same time (filesystem coherency, - * etc.). If you're running an sbp2 device that supports multiple logins, - * and you're either running read-only filesystems or some sort of special - * filesystem supporting multiple hosts, e.g. OpenGFS, Oracle Cluster - * File System, or Lustre, then set exclusive_login to zero. - * - * So far only bridges from Oxford Semiconductor are known to support - * concurrent logins. Depending on firmware, four or two concurrent logins - * are possible on OXFW911 and newer Oxsemi bridges. - */ -static int sbp2_exclusive_login = 1; -module_param_named(exclusive_login, sbp2_exclusive_login, bool, 0644); -MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device " - "(default = Y, use N for concurrent initiators)"); - -/* - * If any of the following workarounds is required for your device to work, - * please submit the kernel messages logged by sbp2 to the linux1394-devel - * mailing list. - * - * - 128kB max transfer - * Limit transfer size. Necessary for some old bridges. - * - * - 36 byte inquiry - * When scsi_mod probes the device, let the inquiry command look like that - * from MS Windows. - * - * - skip mode page 8 - * Suppress sending of mode_sense for mode page 8 if the device pretends to - * support the SCSI Primary Block commands instead of Reduced Block Commands. - * - * - fix capacity - * Tell sd_mod to correct the last sector number reported by read_capacity. - * Avoids access beyond actual disk limits on devices with an off-by-one bug. - * Don't use this with devices which don't have this bug. - * - * - delay inquiry - * Wait extra SBP2_INQUIRY_DELAY seconds after login before SCSI inquiry. - * - * - power condition - * Set the power condition field in the START STOP UNIT commands sent by - * sd_mod on suspend, resume, and shutdown (if manage_start_stop is on). - * Some disks need this to spin down or to resume properly. - * - * - override internal blacklist - * Instead of adding to the built-in blacklist, use only the workarounds - * specified in the module load parameter. - * Useful if a blacklist entry interfered with a non-broken device. - */ -static int sbp2_default_workarounds; -module_param_named(workarounds, sbp2_default_workarounds, int, 0644); -MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0" - ", 128kB max transfer = " __stringify(SBP2_WORKAROUND_128K_MAX_TRANS) - ", 36 byte inquiry = " __stringify(SBP2_WORKAROUND_INQUIRY_36) - ", skip mode page 8 = " __stringify(SBP2_WORKAROUND_MODE_SENSE_8) - ", fix capacity = " __stringify(SBP2_WORKAROUND_FIX_CAPACITY) - ", delay inquiry = " __stringify(SBP2_WORKAROUND_DELAY_INQUIRY) - ", set power condition in start stop unit = " - __stringify(SBP2_WORKAROUND_POWER_CONDITION) - ", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE) - ", or a combination)"); - -/* - * This influences the format of the sysfs attribute - * /sys/bus/scsi/devices/.../ieee1394_id. - * - * The default format is like in older kernels: %016Lx:%d:%d - * It contains the target's EUI-64, a number given to the logical unit by - * the ieee1394 driver's nodemgr (starting at 0), and the LUN. - * - * The long format is: %016Lx:%06x:%04x - * It contains the target's EUI-64, the unit directory's directory_ID as per - * IEEE 1212 clause 7.7.19, and the LUN. This format comes closest to the - * format of SBP(-3) target port and logical unit identifier as per SAM (SCSI - * Architecture Model) rev.2 to 4 annex A. Therefore and because it is - * independent of the implementation of the ieee1394 nodemgr, the longer format - * is recommended for future use. - */ -static int sbp2_long_sysfs_ieee1394_id; -module_param_named(long_ieee1394_id, sbp2_long_sysfs_ieee1394_id, bool, 0644); -MODULE_PARM_DESC(long_ieee1394_id, "8+3+2 bytes format of ieee1394_id in sysfs " - "(default = backwards-compatible = N, SAM-conforming = Y)"); - - -#define SBP2_INFO(fmt, args...) HPSB_INFO("sbp2: "fmt, ## args) -#define SBP2_ERR(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args) - -/* - * Globals - */ -static void sbp2scsi_complete_all_commands(struct sbp2_lu *, u32); -static void sbp2scsi_complete_command(struct sbp2_lu *, u32, struct scsi_cmnd *, - void (*)(struct scsi_cmnd *)); -static struct sbp2_lu *sbp2_alloc_device(struct unit_directory *); -static int sbp2_start_device(struct sbp2_lu *); -static void sbp2_remove_device(struct sbp2_lu *); -static int sbp2_login_device(struct sbp2_lu *); -static int sbp2_reconnect_device(struct sbp2_lu *); -static int sbp2_logout_device(struct sbp2_lu *); -static void sbp2_host_reset(struct hpsb_host *); -static int sbp2_handle_status_write(struct hpsb_host *, int, int, quadlet_t *, - u64, size_t, u16); -static int sbp2_agent_reset(struct sbp2_lu *, int); -static void sbp2_parse_unit_directory(struct sbp2_lu *, - struct unit_directory *); -static int sbp2_set_busy_timeout(struct sbp2_lu *); -static int sbp2_max_speed_and_size(struct sbp2_lu *); - - -static const u8 sbp2_speedto_max_payload[] = { 0x7, 0x8, 0x9, 0xa, 0xa, 0xa }; - -static DEFINE_RWLOCK(sbp2_hi_logical_units_lock); - -static struct hpsb_highlevel sbp2_highlevel = { - .name = SBP2_DEVICE_NAME, - .host_reset = sbp2_host_reset, -}; - -static const struct hpsb_address_ops sbp2_ops = { - .write = sbp2_handle_status_write -}; - -#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA -static int sbp2_handle_physdma_write(struct hpsb_host *, int, int, quadlet_t *, - u64, size_t, u16); -static int sbp2_handle_physdma_read(struct hpsb_host *, int, quadlet_t *, u64, - size_t, u16); - -static const struct hpsb_address_ops sbp2_physdma_ops = { - .read = sbp2_handle_physdma_read, - .write = sbp2_handle_physdma_write, -}; -#endif - - -/* - * Interface to driver core and IEEE 1394 core - */ -static const struct ieee1394_device_id sbp2_id_table[] = { - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = SBP2_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = SBP2_SW_VERSION_ENTRY & 0xffffff}, - {} -}; -MODULE_DEVICE_TABLE(ieee1394, sbp2_id_table); - -static int sbp2_probe(struct device *); -static int sbp2_remove(struct device *); -static int sbp2_update(struct unit_directory *); - -static struct hpsb_protocol_driver sbp2_driver = { - .name = SBP2_DEVICE_NAME, - .id_table = sbp2_id_table, - .update = sbp2_update, - .driver = { - .probe = sbp2_probe, - .remove = sbp2_remove, - }, -}; - - -/* - * Interface to SCSI core - */ -static int sbp2scsi_queuecommand(struct scsi_cmnd *, - void (*)(struct scsi_cmnd *)); -static int sbp2scsi_abort(struct scsi_cmnd *); -static int sbp2scsi_reset(struct scsi_cmnd *); -static int sbp2scsi_slave_alloc(struct scsi_device *); -static int sbp2scsi_slave_configure(struct scsi_device *); -static void sbp2scsi_slave_destroy(struct scsi_device *); -static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *, - struct device_attribute *, char *); - -static DEVICE_ATTR(ieee1394_id, S_IRUGO, sbp2_sysfs_ieee1394_id_show, NULL); - -static struct device_attribute *sbp2_sysfs_sdev_attrs[] = { - &dev_attr_ieee1394_id, - NULL -}; - -static struct scsi_host_template sbp2_shost_template = { - .module = THIS_MODULE, - .name = "SBP-2 IEEE-1394", - .proc_name = SBP2_DEVICE_NAME, - .queuecommand = sbp2scsi_queuecommand, - .eh_abort_handler = sbp2scsi_abort, - .eh_device_reset_handler = sbp2scsi_reset, - .slave_alloc = sbp2scsi_slave_alloc, - .slave_configure = sbp2scsi_slave_configure, - .slave_destroy = sbp2scsi_slave_destroy, - .this_id = -1, - .sg_tablesize = SG_ALL, - .use_clustering = ENABLE_CLUSTERING, - .cmd_per_lun = SBP2_MAX_CMDS, - .can_queue = SBP2_MAX_CMDS, - .sdev_attrs = sbp2_sysfs_sdev_attrs, -}; - -#define SBP2_ROM_VALUE_WILDCARD ~0 /* match all */ -#define SBP2_ROM_VALUE_MISSING 0xff000000 /* not present in the unit dir. */ - -/* - * List of devices with known bugs. - * - * The firmware_revision field, masked with 0xffff00, is the best indicator - * for the type of bridge chip of a device. It yields a few false positives - * but this did not break correctly behaving devices so far. - */ -static const struct { - u32 firmware_revision; - u32 model; - unsigned workarounds; -} sbp2_workarounds_table[] = { - /* DViCO Momobay CX-1 with TSB42AA9 bridge */ { - .firmware_revision = 0x002800, - .model = 0x001010, - .workarounds = SBP2_WORKAROUND_INQUIRY_36 | - SBP2_WORKAROUND_MODE_SENSE_8 | - SBP2_WORKAROUND_POWER_CONDITION, - }, - /* DViCO Momobay FX-3A with TSB42AA9A bridge */ { - .firmware_revision = 0x002800, - .model = 0x000000, - .workarounds = SBP2_WORKAROUND_POWER_CONDITION, - }, - /* Initio bridges, actually only needed for some older ones */ { - .firmware_revision = 0x000200, - .model = SBP2_ROM_VALUE_WILDCARD, - .workarounds = SBP2_WORKAROUND_INQUIRY_36, - }, - /* PL-3507 bridge with Prolific firmware */ { - .firmware_revision = 0x012800, - .model = SBP2_ROM_VALUE_WILDCARD, - .workarounds = SBP2_WORKAROUND_POWER_CONDITION, - }, - /* Symbios bridge */ { - .firmware_revision = 0xa0b800, - .model = SBP2_ROM_VALUE_WILDCARD, - .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, - }, - /* Datafab MD2-FW2 with Symbios/LSILogic SYM13FW500 bridge */ { - .firmware_revision = 0x002600, - .model = SBP2_ROM_VALUE_WILDCARD, - .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, - }, - /* - * iPod 2nd generation: needs 128k max transfer size workaround - * iPod 3rd generation: needs fix capacity workaround - */ - { - .firmware_revision = 0x0a2700, - .model = 0x000000, - .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS | - SBP2_WORKAROUND_FIX_CAPACITY, - }, - /* iPod 4th generation */ { - .firmware_revision = 0x0a2700, - .model = 0x000021, - .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, - }, - /* iPod mini */ { - .firmware_revision = 0x0a2700, - .model = 0x000022, - .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, - }, - /* iPod mini */ { - .firmware_revision = 0x0a2700, - .model = 0x000023, - .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, - }, - /* iPod Photo */ { - .firmware_revision = 0x0a2700, - .model = 0x00007e, - .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, - } -}; - -/************************************** - * General utility functions - **************************************/ - -#ifndef __BIG_ENDIAN -/* - * Converts a buffer from be32 to cpu byte ordering. Length is in bytes. - */ -static inline void sbp2util_be32_to_cpu_buffer(void *buffer, int length) -{ - u32 *temp = buffer; - - for (length = (length >> 2); length--; ) - temp[length] = be32_to_cpu(temp[length]); -} - -/* - * Converts a buffer from cpu to be32 byte ordering. Length is in bytes. - */ -static inline void sbp2util_cpu_to_be32_buffer(void *buffer, int length) -{ - u32 *temp = buffer; - - for (length = (length >> 2); length--; ) - temp[length] = cpu_to_be32(temp[length]); -} -#else /* BIG_ENDIAN */ -/* Why waste the cpu cycles? */ -#define sbp2util_be32_to_cpu_buffer(x,y) do {} while (0) -#define sbp2util_cpu_to_be32_buffer(x,y) do {} while (0) -#endif - -static DECLARE_WAIT_QUEUE_HEAD(sbp2_access_wq); - -/* - * Waits for completion of an SBP-2 access request. - * Returns nonzero if timed out or prematurely interrupted. - */ -static int sbp2util_access_timeout(struct sbp2_lu *lu, int timeout) -{ - long leftover; - - leftover = wait_event_interruptible_timeout( - sbp2_access_wq, lu->access_complete, timeout); - lu->access_complete = 0; - return leftover <= 0; -} - -static void sbp2_free_packet(void *packet) -{ - hpsb_free_tlabel(packet); - hpsb_free_packet(packet); -} - -/* - * This is much like hpsb_node_write(), except it ignores the response - * subaction and returns immediately. Can be used from atomic context. - */ -static int sbp2util_node_write_no_wait(struct node_entry *ne, u64 addr, - quadlet_t *buf, size_t len) -{ - struct hpsb_packet *packet; - - packet = hpsb_make_writepacket(ne->host, ne->nodeid, addr, buf, len); - if (!packet) - return -ENOMEM; - - hpsb_set_packet_complete_task(packet, sbp2_free_packet, packet); - hpsb_node_fill_packet(ne, packet); - if (hpsb_send_packet(packet) < 0) { - sbp2_free_packet(packet); - return -EIO; - } - return 0; -} - -static void sbp2util_notify_fetch_agent(struct sbp2_lu *lu, u64 offset, - quadlet_t *data, size_t len) -{ - /* There is a small window after a bus reset within which the node - * entry's generation is current but the reconnect wasn't completed. */ - if (unlikely(atomic_read(&lu->state) == SBP2LU_STATE_IN_RESET)) - return; - - if (hpsb_node_write(lu->ne, lu->command_block_agent_addr + offset, - data, len)) - SBP2_ERR("sbp2util_notify_fetch_agent failed."); - - /* Now accept new SCSI commands, unless a bus reset happended during - * hpsb_node_write. */ - if (likely(atomic_read(&lu->state) != SBP2LU_STATE_IN_RESET)) - scsi_unblock_requests(lu->shost); -} - -static void sbp2util_write_orb_pointer(struct work_struct *work) -{ - struct sbp2_lu *lu = container_of(work, struct sbp2_lu, protocol_work); - quadlet_t data[2]; - - data[0] = ORB_SET_NODE_ID(lu->hi->host->node_id); - data[1] = lu->last_orb_dma; - sbp2util_cpu_to_be32_buffer(data, 8); - sbp2util_notify_fetch_agent(lu, SBP2_ORB_POINTER_OFFSET, data, 8); -} - -static void sbp2util_write_doorbell(struct work_struct *work) -{ - struct sbp2_lu *lu = container_of(work, struct sbp2_lu, protocol_work); - - sbp2util_notify_fetch_agent(lu, SBP2_DOORBELL_OFFSET, NULL, 4); -} - -static int sbp2util_create_command_orb_pool(struct sbp2_lu *lu) -{ - struct sbp2_command_info *cmd; - struct device *dmadev = lu->hi->host->device.parent; - int i, orbs = sbp2_serialize_io ? 2 : SBP2_MAX_CMDS; - - for (i = 0; i < orbs; i++) { - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (!cmd) - goto failed_alloc; - - cmd->command_orb_dma = - dma_map_single(dmadev, &cmd->command_orb, - sizeof(struct sbp2_command_orb), - DMA_TO_DEVICE); - if (dma_mapping_error(dmadev, cmd->command_orb_dma)) - goto failed_orb; - - cmd->sge_dma = - dma_map_single(dmadev, &cmd->scatter_gather_element, - sizeof(cmd->scatter_gather_element), - DMA_TO_DEVICE); - if (dma_mapping_error(dmadev, cmd->sge_dma)) - goto failed_sge; - - INIT_LIST_HEAD(&cmd->list); - list_add_tail(&cmd->list, &lu->cmd_orb_completed); - } - return 0; - -failed_sge: - dma_unmap_single(dmadev, cmd->command_orb_dma, - sizeof(struct sbp2_command_orb), DMA_TO_DEVICE); -failed_orb: - kfree(cmd); -failed_alloc: - return -ENOMEM; -} - -static void sbp2util_remove_command_orb_pool(struct sbp2_lu *lu, - struct hpsb_host *host) -{ - struct list_head *lh, *next; - struct sbp2_command_info *cmd; - unsigned long flags; - - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - if (!list_empty(&lu->cmd_orb_completed)) - list_for_each_safe(lh, next, &lu->cmd_orb_completed) { - cmd = list_entry(lh, struct sbp2_command_info, list); - dma_unmap_single(host->device.parent, - cmd->command_orb_dma, - sizeof(struct sbp2_command_orb), - DMA_TO_DEVICE); - dma_unmap_single(host->device.parent, cmd->sge_dma, - sizeof(cmd->scatter_gather_element), - DMA_TO_DEVICE); - kfree(cmd); - } - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - return; -} - -/* - * Finds the sbp2_command for a given outstanding command ORB. - * Only looks at the in-use list. - */ -static struct sbp2_command_info *sbp2util_find_command_for_orb( - struct sbp2_lu *lu, dma_addr_t orb) -{ - struct sbp2_command_info *cmd; - unsigned long flags; - - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - if (!list_empty(&lu->cmd_orb_inuse)) - list_for_each_entry(cmd, &lu->cmd_orb_inuse, list) - if (cmd->command_orb_dma == orb) { - spin_unlock_irqrestore( - &lu->cmd_orb_lock, flags); - return cmd; - } - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - return NULL; -} - -/* - * Finds the sbp2_command for a given outstanding SCpnt. - * Only looks at the in-use list. - * Must be called with lu->cmd_orb_lock held. - */ -static struct sbp2_command_info *sbp2util_find_command_for_SCpnt( - struct sbp2_lu *lu, void *SCpnt) -{ - struct sbp2_command_info *cmd; - - if (!list_empty(&lu->cmd_orb_inuse)) - list_for_each_entry(cmd, &lu->cmd_orb_inuse, list) - if (cmd->Current_SCpnt == SCpnt) - return cmd; - return NULL; -} - -static struct sbp2_command_info *sbp2util_allocate_command_orb( - struct sbp2_lu *lu, - struct scsi_cmnd *Current_SCpnt, - void (*Current_done)(struct scsi_cmnd *)) -{ - struct list_head *lh; - struct sbp2_command_info *cmd = NULL; - unsigned long flags; - - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - if (!list_empty(&lu->cmd_orb_completed)) { - lh = lu->cmd_orb_completed.next; - list_del(lh); - cmd = list_entry(lh, struct sbp2_command_info, list); - cmd->Current_done = Current_done; - cmd->Current_SCpnt = Current_SCpnt; - list_add_tail(&cmd->list, &lu->cmd_orb_inuse); - } else - SBP2_ERR("%s: no orbs available", __func__); - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - return cmd; -} - -/* - * Unmaps the DMAs of a command and moves the command to the completed ORB list. - * Must be called with lu->cmd_orb_lock held. - */ -static void sbp2util_mark_command_completed(struct sbp2_lu *lu, - struct sbp2_command_info *cmd) -{ - if (scsi_sg_count(cmd->Current_SCpnt)) - dma_unmap_sg(lu->ud->ne->host->device.parent, - scsi_sglist(cmd->Current_SCpnt), - scsi_sg_count(cmd->Current_SCpnt), - cmd->Current_SCpnt->sc_data_direction); - list_move_tail(&cmd->list, &lu->cmd_orb_completed); -} - -/* - * Is lu valid? Is the 1394 node still present? - */ -static inline int sbp2util_node_is_available(struct sbp2_lu *lu) -{ - return lu && lu->ne && !lu->ne->in_limbo; -} - -/********************************************* - * IEEE-1394 core driver stack related section - *********************************************/ - -static int sbp2_probe(struct device *dev) -{ - struct unit_directory *ud; - struct sbp2_lu *lu; - - ud = container_of(dev, struct unit_directory, device); - - /* Don't probe UD's that have the LUN flag. We'll probe the LUN(s) - * instead. */ - if (ud->flags & UNIT_DIRECTORY_HAS_LUN_DIRECTORY) - return -ENODEV; - - lu = sbp2_alloc_device(ud); - if (!lu) - return -ENOMEM; - - sbp2_parse_unit_directory(lu, ud); - return sbp2_start_device(lu); -} - -static int sbp2_remove(struct device *dev) -{ - struct unit_directory *ud; - struct sbp2_lu *lu; - struct scsi_device *sdev; - - ud = container_of(dev, struct unit_directory, device); - lu = dev_get_drvdata(&ud->device); - if (!lu) - return 0; - - if (lu->shost) { - /* Get rid of enqueued commands if there is no chance to - * send them. */ - if (!sbp2util_node_is_available(lu)) - sbp2scsi_complete_all_commands(lu, DID_NO_CONNECT); - /* scsi_remove_device() may trigger shutdown functions of SCSI - * highlevel drivers which would deadlock if blocked. */ - atomic_set(&lu->state, SBP2LU_STATE_IN_SHUTDOWN); - scsi_unblock_requests(lu->shost); - } - sdev = lu->sdev; - if (sdev) { - lu->sdev = NULL; - scsi_remove_device(sdev); - } - - sbp2_logout_device(lu); - sbp2_remove_device(lu); - - return 0; -} - -static int sbp2_update(struct unit_directory *ud) -{ - struct sbp2_lu *lu = dev_get_drvdata(&ud->device); - - if (sbp2_reconnect_device(lu) != 0) { - /* - * Reconnect failed. If another bus reset happened, - * let nodemgr proceed and call sbp2_update again later - * (or sbp2_remove if this node went away). - */ - if (!hpsb_node_entry_valid(lu->ne)) - return 0; - /* - * Or the target rejected the reconnect because we weren't - * fast enough. Try a regular login, but first log out - * just in case of any weirdness. - */ - sbp2_logout_device(lu); - - if (sbp2_login_device(lu) != 0) { - if (!hpsb_node_entry_valid(lu->ne)) - return 0; - - /* Maybe another initiator won the login. */ - SBP2_ERR("Failed to reconnect to sbp2 device!"); - return -EBUSY; - } - } - - sbp2_set_busy_timeout(lu); - sbp2_agent_reset(lu, 1); - sbp2_max_speed_and_size(lu); - - /* Complete any pending commands with busy (so they get retried) - * and remove them from our queue. */ - sbp2scsi_complete_all_commands(lu, DID_BUS_BUSY); - - /* Accept new commands unless there was another bus reset in the - * meantime. */ - if (hpsb_node_entry_valid(lu->ne)) { - atomic_set(&lu->state, SBP2LU_STATE_RUNNING); - scsi_unblock_requests(lu->shost); - } - return 0; -} - -static struct sbp2_lu *sbp2_alloc_device(struct unit_directory *ud) -{ - struct sbp2_fwhost_info *hi; - struct Scsi_Host *shost = NULL; - struct sbp2_lu *lu = NULL; - unsigned long flags; - - lu = kzalloc(sizeof(*lu), GFP_KERNEL); - if (!lu) { - SBP2_ERR("failed to create lu"); - goto failed_alloc; - } - - lu->ne = ud->ne; - lu->ud = ud; - lu->speed_code = IEEE1394_SPEED_100; - lu->max_payload_size = sbp2_speedto_max_payload[IEEE1394_SPEED_100]; - lu->status_fifo_addr = CSR1212_INVALID_ADDR_SPACE; - INIT_LIST_HEAD(&lu->cmd_orb_inuse); - INIT_LIST_HEAD(&lu->cmd_orb_completed); - INIT_LIST_HEAD(&lu->lu_list); - spin_lock_init(&lu->cmd_orb_lock); - atomic_set(&lu->state, SBP2LU_STATE_RUNNING); - INIT_WORK(&lu->protocol_work, NULL); - - dev_set_drvdata(&ud->device, lu); - - hi = hpsb_get_hostinfo(&sbp2_highlevel, ud->ne->host); - if (!hi) { - hi = hpsb_create_hostinfo(&sbp2_highlevel, ud->ne->host, - sizeof(*hi)); - if (!hi) { - SBP2_ERR("failed to allocate hostinfo"); - goto failed_alloc; - } - hi->host = ud->ne->host; - INIT_LIST_HEAD(&hi->logical_units); - -#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA - /* Handle data movement if physical dma is not - * enabled or not supported on host controller */ - if (!hpsb_register_addrspace(&sbp2_highlevel, ud->ne->host, - &sbp2_physdma_ops, - 0x0ULL, 0xfffffffcULL)) { - SBP2_ERR("failed to register lower 4GB address range"); - goto failed_alloc; - } -#endif - } - - if (dma_get_max_seg_size(hi->host->device.parent) > SBP2_MAX_SEG_SIZE) - BUG_ON(dma_set_max_seg_size(hi->host->device.parent, - SBP2_MAX_SEG_SIZE)); - - /* Prevent unloading of the 1394 host */ - if (!try_module_get(hi->host->driver->owner)) { - SBP2_ERR("failed to get a reference on 1394 host driver"); - goto failed_alloc; - } - - lu->hi = hi; - - write_lock_irqsave(&sbp2_hi_logical_units_lock, flags); - list_add_tail(&lu->lu_list, &hi->logical_units); - write_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); - - /* Register the status FIFO address range. We could use the same FIFO - * for targets at different nodes. However we need different FIFOs per - * target in order to support multi-unit devices. - * The FIFO is located out of the local host controller's physical range - * but, if possible, within the posted write area. Status writes will - * then be performed as unified transactions. This slightly reduces - * bandwidth usage, and some Prolific based devices seem to require it. - */ - lu->status_fifo_addr = hpsb_allocate_and_register_addrspace( - &sbp2_highlevel, ud->ne->host, &sbp2_ops, - sizeof(struct sbp2_status_block), sizeof(quadlet_t), - ud->ne->host->low_addr_space, CSR1212_ALL_SPACE_END); - if (lu->status_fifo_addr == CSR1212_INVALID_ADDR_SPACE) { - SBP2_ERR("failed to allocate status FIFO address range"); - goto failed_alloc; - } - - shost = scsi_host_alloc(&sbp2_shost_template, sizeof(unsigned long)); - if (!shost) { - SBP2_ERR("failed to register scsi host"); - goto failed_alloc; - } - - shost->hostdata[0] = (unsigned long)lu; - shost->max_cmd_len = SBP2_MAX_CDB_SIZE; - - if (!scsi_add_host(shost, &ud->device)) { - lu->shost = shost; - return lu; - } - - SBP2_ERR("failed to add scsi host"); - scsi_host_put(shost); - -failed_alloc: - sbp2_remove_device(lu); - return NULL; -} - -static void sbp2_host_reset(struct hpsb_host *host) -{ - struct sbp2_fwhost_info *hi; - struct sbp2_lu *lu; - unsigned long flags; - - hi = hpsb_get_hostinfo(&sbp2_highlevel, host); - if (!hi) - return; - - read_lock_irqsave(&sbp2_hi_logical_units_lock, flags); - - list_for_each_entry(lu, &hi->logical_units, lu_list) - if (atomic_cmpxchg(&lu->state, - SBP2LU_STATE_RUNNING, SBP2LU_STATE_IN_RESET) - == SBP2LU_STATE_RUNNING) - scsi_block_requests(lu->shost); - - read_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); -} - -static int sbp2_start_device(struct sbp2_lu *lu) -{ - struct sbp2_fwhost_info *hi = lu->hi; - int error; - - lu->login_response = dma_alloc_coherent(hi->host->device.parent, - sizeof(struct sbp2_login_response), - &lu->login_response_dma, GFP_KERNEL); - if (!lu->login_response) - goto alloc_fail; - - lu->query_logins_orb = dma_alloc_coherent(hi->host->device.parent, - sizeof(struct sbp2_query_logins_orb), - &lu->query_logins_orb_dma, GFP_KERNEL); - if (!lu->query_logins_orb) - goto alloc_fail; - - lu->query_logins_response = dma_alloc_coherent(hi->host->device.parent, - sizeof(struct sbp2_query_logins_response), - &lu->query_logins_response_dma, GFP_KERNEL); - if (!lu->query_logins_response) - goto alloc_fail; - - lu->reconnect_orb = dma_alloc_coherent(hi->host->device.parent, - sizeof(struct sbp2_reconnect_orb), - &lu->reconnect_orb_dma, GFP_KERNEL); - if (!lu->reconnect_orb) - goto alloc_fail; - - lu->logout_orb = dma_alloc_coherent(hi->host->device.parent, - sizeof(struct sbp2_logout_orb), - &lu->logout_orb_dma, GFP_KERNEL); - if (!lu->logout_orb) - goto alloc_fail; - - lu->login_orb = dma_alloc_coherent(hi->host->device.parent, - sizeof(struct sbp2_login_orb), - &lu->login_orb_dma, GFP_KERNEL); - if (!lu->login_orb) - goto alloc_fail; - - if (sbp2util_create_command_orb_pool(lu)) - goto alloc_fail; - - /* Wait a second before trying to log in. Previously logged in - * initiators need a chance to reconnect. */ - if (msleep_interruptible(1000)) { - sbp2_remove_device(lu); - return -EINTR; - } - - if (sbp2_login_device(lu)) { - sbp2_remove_device(lu); - return -EBUSY; - } - - sbp2_set_busy_timeout(lu); - sbp2_agent_reset(lu, 1); - sbp2_max_speed_and_size(lu); - - if (lu->workarounds & SBP2_WORKAROUND_DELAY_INQUIRY) - ssleep(SBP2_INQUIRY_DELAY); - - error = scsi_add_device(lu->shost, 0, lu->ud->id, 0); - if (error) { - SBP2_ERR("scsi_add_device failed"); - sbp2_logout_device(lu); - sbp2_remove_device(lu); - return error; - } - - return 0; - -alloc_fail: - SBP2_ERR("Could not allocate memory for lu"); - sbp2_remove_device(lu); - return -ENOMEM; -} - -static void sbp2_remove_device(struct sbp2_lu *lu) -{ - struct sbp2_fwhost_info *hi; - unsigned long flags; - - if (!lu) - return; - hi = lu->hi; - if (!hi) - goto no_hi; - - if (lu->shost) { - scsi_remove_host(lu->shost); - scsi_host_put(lu->shost); - } - flush_scheduled_work(); - sbp2util_remove_command_orb_pool(lu, hi->host); - - write_lock_irqsave(&sbp2_hi_logical_units_lock, flags); - list_del(&lu->lu_list); - write_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); - - if (lu->login_response) - dma_free_coherent(hi->host->device.parent, - sizeof(struct sbp2_login_response), - lu->login_response, - lu->login_response_dma); - if (lu->login_orb) - dma_free_coherent(hi->host->device.parent, - sizeof(struct sbp2_login_orb), - lu->login_orb, - lu->login_orb_dma); - if (lu->reconnect_orb) - dma_free_coherent(hi->host->device.parent, - sizeof(struct sbp2_reconnect_orb), - lu->reconnect_orb, - lu->reconnect_orb_dma); - if (lu->logout_orb) - dma_free_coherent(hi->host->device.parent, - sizeof(struct sbp2_logout_orb), - lu->logout_orb, - lu->logout_orb_dma); - if (lu->query_logins_orb) - dma_free_coherent(hi->host->device.parent, - sizeof(struct sbp2_query_logins_orb), - lu->query_logins_orb, - lu->query_logins_orb_dma); - if (lu->query_logins_response) - dma_free_coherent(hi->host->device.parent, - sizeof(struct sbp2_query_logins_response), - lu->query_logins_response, - lu->query_logins_response_dma); - - if (lu->status_fifo_addr != CSR1212_INVALID_ADDR_SPACE) - hpsb_unregister_addrspace(&sbp2_highlevel, hi->host, - lu->status_fifo_addr); - - dev_set_drvdata(&lu->ud->device, NULL); - - module_put(hi->host->driver->owner); -no_hi: - kfree(lu); -} - -#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA -/* - * Deal with write requests on adapters which do not support physical DMA or - * have it switched off. - */ -static int sbp2_handle_physdma_write(struct hpsb_host *host, int nodeid, - int destid, quadlet_t *data, u64 addr, - size_t length, u16 flags) -{ - memcpy(bus_to_virt((u32) addr), data, length); - return RCODE_COMPLETE; -} - -/* - * Deal with read requests on adapters which do not support physical DMA or - * have it switched off. - */ -static int sbp2_handle_physdma_read(struct hpsb_host *host, int nodeid, - quadlet_t *data, u64 addr, size_t length, - u16 flags) -{ - memcpy(data, bus_to_virt((u32) addr), length); - return RCODE_COMPLETE; -} -#endif - -/************************************** - * SBP-2 protocol related section - **************************************/ - -static int sbp2_query_logins(struct sbp2_lu *lu) -{ - struct sbp2_fwhost_info *hi = lu->hi; - quadlet_t data[2]; - int max_logins; - int active_logins; - - lu->query_logins_orb->reserved1 = 0x0; - lu->query_logins_orb->reserved2 = 0x0; - - lu->query_logins_orb->query_response_lo = lu->query_logins_response_dma; - lu->query_logins_orb->query_response_hi = - ORB_SET_NODE_ID(hi->host->node_id); - lu->query_logins_orb->lun_misc = - ORB_SET_FUNCTION(SBP2_QUERY_LOGINS_REQUEST); - lu->query_logins_orb->lun_misc |= ORB_SET_NOTIFY(1); - lu->query_logins_orb->lun_misc |= ORB_SET_LUN(lu->lun); - - lu->query_logins_orb->reserved_resp_length = - ORB_SET_QUERY_LOGINS_RESP_LENGTH( - sizeof(struct sbp2_query_logins_response)); - - lu->query_logins_orb->status_fifo_hi = - ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id); - lu->query_logins_orb->status_fifo_lo = - ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr); - - sbp2util_cpu_to_be32_buffer(lu->query_logins_orb, - sizeof(struct sbp2_query_logins_orb)); - - memset(lu->query_logins_response, 0, - sizeof(struct sbp2_query_logins_response)); - - data[0] = ORB_SET_NODE_ID(hi->host->node_id); - data[1] = lu->query_logins_orb_dma; - sbp2util_cpu_to_be32_buffer(data, 8); - - hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8); - - if (sbp2util_access_timeout(lu, 2*HZ)) { - SBP2_INFO("Error querying logins to SBP-2 device - timed out"); - return -EIO; - } - - if (lu->status_block.ORB_offset_lo != lu->query_logins_orb_dma) { - SBP2_INFO("Error querying logins to SBP-2 device - timed out"); - return -EIO; - } - - if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) { - SBP2_INFO("Error querying logins to SBP-2 device - failed"); - return -EIO; - } - - sbp2util_cpu_to_be32_buffer(lu->query_logins_response, - sizeof(struct sbp2_query_logins_response)); - - max_logins = RESPONSE_GET_MAX_LOGINS( - lu->query_logins_response->length_max_logins); - SBP2_INFO("Maximum concurrent logins supported: %d", max_logins); - - active_logins = RESPONSE_GET_ACTIVE_LOGINS( - lu->query_logins_response->length_max_logins); - SBP2_INFO("Number of active logins: %d", active_logins); - - if (active_logins >= max_logins) { - return -EIO; - } - - return 0; -} - -static int sbp2_login_device(struct sbp2_lu *lu) -{ - struct sbp2_fwhost_info *hi = lu->hi; - quadlet_t data[2]; - - if (!lu->login_orb) - return -EIO; - - if (!sbp2_exclusive_login && sbp2_query_logins(lu)) { - SBP2_INFO("Device does not support any more concurrent logins"); - return -EIO; - } - - /* assume no password */ - lu->login_orb->password_hi = 0; - lu->login_orb->password_lo = 0; - - lu->login_orb->login_response_lo = lu->login_response_dma; - lu->login_orb->login_response_hi = ORB_SET_NODE_ID(hi->host->node_id); - lu->login_orb->lun_misc = ORB_SET_FUNCTION(SBP2_LOGIN_REQUEST); - - /* one second reconnect time */ - lu->login_orb->lun_misc |= ORB_SET_RECONNECT(0); - lu->login_orb->lun_misc |= ORB_SET_EXCLUSIVE(sbp2_exclusive_login); - lu->login_orb->lun_misc |= ORB_SET_NOTIFY(1); - lu->login_orb->lun_misc |= ORB_SET_LUN(lu->lun); - - lu->login_orb->passwd_resp_lengths = - ORB_SET_LOGIN_RESP_LENGTH(sizeof(struct sbp2_login_response)); - - lu->login_orb->status_fifo_hi = - ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id); - lu->login_orb->status_fifo_lo = - ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr); - - sbp2util_cpu_to_be32_buffer(lu->login_orb, - sizeof(struct sbp2_login_orb)); - - memset(lu->login_response, 0, sizeof(struct sbp2_login_response)); - - data[0] = ORB_SET_NODE_ID(hi->host->node_id); - data[1] = lu->login_orb_dma; - sbp2util_cpu_to_be32_buffer(data, 8); - - hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8); - - /* wait up to 20 seconds for login status */ - if (sbp2util_access_timeout(lu, 20*HZ)) { - SBP2_ERR("Error logging into SBP-2 device - timed out"); - return -EIO; - } - - /* make sure that the returned status matches the login ORB */ - if (lu->status_block.ORB_offset_lo != lu->login_orb_dma) { - SBP2_ERR("Error logging into SBP-2 device - timed out"); - return -EIO; - } - - if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) { - SBP2_ERR("Error logging into SBP-2 device - failed"); - return -EIO; - } - - sbp2util_cpu_to_be32_buffer(lu->login_response, - sizeof(struct sbp2_login_response)); - lu->command_block_agent_addr = - ((u64)lu->login_response->command_block_agent_hi) << 32; - lu->command_block_agent_addr |= - ((u64)lu->login_response->command_block_agent_lo); - lu->command_block_agent_addr &= 0x0000ffffffffffffULL; - - SBP2_INFO("Logged into SBP-2 device"); - return 0; -} - -static int sbp2_logout_device(struct sbp2_lu *lu) -{ - struct sbp2_fwhost_info *hi = lu->hi; - quadlet_t data[2]; - int error; - - lu->logout_orb->reserved1 = 0x0; - lu->logout_orb->reserved2 = 0x0; - lu->logout_orb->reserved3 = 0x0; - lu->logout_orb->reserved4 = 0x0; - - lu->logout_orb->login_ID_misc = ORB_SET_FUNCTION(SBP2_LOGOUT_REQUEST); - lu->logout_orb->login_ID_misc |= - ORB_SET_LOGIN_ID(lu->login_response->length_login_ID); - lu->logout_orb->login_ID_misc |= ORB_SET_NOTIFY(1); - - lu->logout_orb->reserved5 = 0x0; - lu->logout_orb->status_fifo_hi = - ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id); - lu->logout_orb->status_fifo_lo = - ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr); - - sbp2util_cpu_to_be32_buffer(lu->logout_orb, - sizeof(struct sbp2_logout_orb)); - - data[0] = ORB_SET_NODE_ID(hi->host->node_id); - data[1] = lu->logout_orb_dma; - sbp2util_cpu_to_be32_buffer(data, 8); - - error = hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8); - if (error) - return error; - - /* wait up to 1 second for the device to complete logout */ - if (sbp2util_access_timeout(lu, HZ)) - return -EIO; - - SBP2_INFO("Logged out of SBP-2 device"); - return 0; -} - -static int sbp2_reconnect_device(struct sbp2_lu *lu) -{ - struct sbp2_fwhost_info *hi = lu->hi; - quadlet_t data[2]; - int error; - - lu->reconnect_orb->reserved1 = 0x0; - lu->reconnect_orb->reserved2 = 0x0; - lu->reconnect_orb->reserved3 = 0x0; - lu->reconnect_orb->reserved4 = 0x0; - - lu->reconnect_orb->login_ID_misc = - ORB_SET_FUNCTION(SBP2_RECONNECT_REQUEST); - lu->reconnect_orb->login_ID_misc |= - ORB_SET_LOGIN_ID(lu->login_response->length_login_ID); - lu->reconnect_orb->login_ID_misc |= ORB_SET_NOTIFY(1); - - lu->reconnect_orb->reserved5 = 0x0; - lu->reconnect_orb->status_fifo_hi = - ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id); - lu->reconnect_orb->status_fifo_lo = - ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr); - - sbp2util_cpu_to_be32_buffer(lu->reconnect_orb, - sizeof(struct sbp2_reconnect_orb)); - - data[0] = ORB_SET_NODE_ID(hi->host->node_id); - data[1] = lu->reconnect_orb_dma; - sbp2util_cpu_to_be32_buffer(data, 8); - - error = hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8); - if (error) - return error; - - /* wait up to 1 second for reconnect status */ - if (sbp2util_access_timeout(lu, HZ)) { - SBP2_ERR("Error reconnecting to SBP-2 device - timed out"); - return -EIO; - } - - /* make sure that the returned status matches the reconnect ORB */ - if (lu->status_block.ORB_offset_lo != lu->reconnect_orb_dma) { - SBP2_ERR("Error reconnecting to SBP-2 device - timed out"); - return -EIO; - } - - if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) { - SBP2_ERR("Error reconnecting to SBP-2 device - failed"); - return -EIO; - } - - SBP2_INFO("Reconnected to SBP-2 device"); - return 0; -} - -/* - * Set the target node's Single Phase Retry limit. Affects the target's retry - * behaviour if our node is too busy to accept requests. - */ -static int sbp2_set_busy_timeout(struct sbp2_lu *lu) -{ - quadlet_t data; - - data = cpu_to_be32(SBP2_BUSY_TIMEOUT_VALUE); - if (hpsb_node_write(lu->ne, SBP2_BUSY_TIMEOUT_ADDRESS, &data, 4)) - SBP2_ERR("%s error", __func__); - return 0; -} - -static void sbp2_parse_unit_directory(struct sbp2_lu *lu, - struct unit_directory *ud) -{ - struct csr1212_keyval *kv; - struct csr1212_dentry *dentry; - u64 management_agent_addr; - u32 firmware_revision, model; - unsigned workarounds; - int i; - - management_agent_addr = 0; - firmware_revision = SBP2_ROM_VALUE_MISSING; - model = ud->flags & UNIT_DIRECTORY_MODEL_ID ? - ud->model_id : SBP2_ROM_VALUE_MISSING; - - csr1212_for_each_dir_entry(ud->ne->csr, kv, ud->ud_kv, dentry) { - switch (kv->key.id) { - case CSR1212_KV_ID_DEPENDENT_INFO: - if (kv->key.type == CSR1212_KV_TYPE_CSR_OFFSET) - management_agent_addr = - CSR1212_REGISTER_SPACE_BASE + - (kv->value.csr_offset << 2); - - else if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) - lu->lun = ORB_SET_LUN(kv->value.immediate); - break; - - - case SBP2_FIRMWARE_REVISION_KEY: - firmware_revision = kv->value.immediate; - break; - - default: - /* FIXME: Check for SBP2_UNIT_CHARACTERISTICS_KEY - * mgt_ORB_timeout and ORB_size, SBP-2 clause 7.4.8. */ - - /* FIXME: Check for SBP2_DEVICE_TYPE_AND_LUN_KEY. - * Its "ordered" bit has consequences for command ORB - * list handling. See SBP-2 clauses 4.6, 7.4.11, 10.2 */ - break; - } - } - - workarounds = sbp2_default_workarounds; - - if (!(workarounds & SBP2_WORKAROUND_OVERRIDE)) - for (i = 0; i < ARRAY_SIZE(sbp2_workarounds_table); i++) { - if (sbp2_workarounds_table[i].firmware_revision != - SBP2_ROM_VALUE_WILDCARD && - sbp2_workarounds_table[i].firmware_revision != - (firmware_revision & 0xffff00)) - continue; - if (sbp2_workarounds_table[i].model != - SBP2_ROM_VALUE_WILDCARD && - sbp2_workarounds_table[i].model != model) - continue; - workarounds |= sbp2_workarounds_table[i].workarounds; - break; - } - - if (workarounds) - SBP2_INFO("Workarounds for node " NODE_BUS_FMT ": 0x%x " - "(firmware_revision 0x%06x, vendor_id 0x%06x," - " model_id 0x%06x)", - NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid), - workarounds, firmware_revision, ud->vendor_id, - model); - - /* We would need one SCSI host template for each target to adjust - * max_sectors on the fly, therefore warn only. */ - if (workarounds & SBP2_WORKAROUND_128K_MAX_TRANS && - (sbp2_max_sectors * 512) > (128 * 1024)) - SBP2_INFO("Node " NODE_BUS_FMT ": Bridge only supports 128KB " - "max transfer size. WARNING: Current max_sectors " - "setting is larger than 128KB (%d sectors)", - NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid), - sbp2_max_sectors); - - /* If this is a logical unit directory entry, process the parent - * to get the values. */ - if (ud->flags & UNIT_DIRECTORY_LUN_DIRECTORY) { - struct unit_directory *parent_ud = container_of( - ud->device.parent, struct unit_directory, device); - sbp2_parse_unit_directory(lu, parent_ud); - } else { - lu->management_agent_addr = management_agent_addr; - lu->workarounds = workarounds; - if (ud->flags & UNIT_DIRECTORY_HAS_LUN) - lu->lun = ORB_SET_LUN(ud->lun); - } -} - -#define SBP2_PAYLOAD_TO_BYTES(p) (1 << ((p) + 2)) - -/* - * This function is called in order to determine the max speed and packet - * size we can use in our ORBs. Note, that we (the driver and host) only - * initiate the transaction. The SBP-2 device actually transfers the data - * (by reading from the DMA area we tell it). This means that the SBP-2 - * device decides the actual maximum data it can transfer. We just tell it - * the speed that it needs to use, and the max_rec the host supports, and - * it takes care of the rest. - */ -static int sbp2_max_speed_and_size(struct sbp2_lu *lu) -{ - struct sbp2_fwhost_info *hi = lu->hi; - u8 payload; - - lu->speed_code = hi->host->speed[NODEID_TO_NODE(lu->ne->nodeid)]; - - if (lu->speed_code > sbp2_max_speed) { - lu->speed_code = sbp2_max_speed; - SBP2_INFO("Reducing speed to %s", - hpsb_speedto_str[sbp2_max_speed]); - } - - /* Payload size is the lesser of what our speed supports and what - * our host supports. */ - payload = min(sbp2_speedto_max_payload[lu->speed_code], - (u8) (hi->host->csr.max_rec - 1)); - - /* If physical DMA is off, work around limitation in ohci1394: - * packet size must not exceed PAGE_SIZE */ - if (lu->ne->host->low_addr_space < (1ULL << 32)) - while (SBP2_PAYLOAD_TO_BYTES(payload) + 24 > PAGE_SIZE && - payload) - payload--; - - SBP2_INFO("Node " NODE_BUS_FMT ": Max speed [%s] - Max payload [%u]", - NODE_BUS_ARGS(hi->host, lu->ne->nodeid), - hpsb_speedto_str[lu->speed_code], - SBP2_PAYLOAD_TO_BYTES(payload)); - - lu->max_payload_size = payload; - return 0; -} - -static int sbp2_agent_reset(struct sbp2_lu *lu, int wait) -{ - quadlet_t data; - u64 addr; - int retval; - unsigned long flags; - - /* flush lu->protocol_work */ - if (wait) - flush_scheduled_work(); - - data = ntohl(SBP2_AGENT_RESET_DATA); - addr = lu->command_block_agent_addr + SBP2_AGENT_RESET_OFFSET; - - if (wait) - retval = hpsb_node_write(lu->ne, addr, &data, 4); - else - retval = sbp2util_node_write_no_wait(lu->ne, addr, &data, 4); - - if (retval < 0) { - SBP2_ERR("hpsb_node_write failed.\n"); - return -EIO; - } - - /* make sure that the ORB_POINTER is written on next command */ - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - lu->last_orb = NULL; - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - - return 0; -} - -static int sbp2_prep_command_orb_sg(struct sbp2_command_orb *orb, - struct sbp2_fwhost_info *hi, - struct sbp2_command_info *cmd, - unsigned int sg_count, - struct scatterlist *sg, - u32 orb_direction, - enum dma_data_direction dma_dir) -{ - struct device *dmadev = hi->host->device.parent; - struct sbp2_unrestricted_page_table *pt; - int i, n; - - n = dma_map_sg(dmadev, sg, sg_count, dma_dir); - if (n == 0) - return -ENOMEM; - - orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); - orb->misc |= ORB_SET_DIRECTION(orb_direction); - - /* special case if only one element (and less than 64KB in size) */ - if (n == 1) { - orb->misc |= ORB_SET_DATA_SIZE(sg_dma_len(sg)); - orb->data_descriptor_lo = sg_dma_address(sg); - } else { - pt = &cmd->scatter_gather_element[0]; - - dma_sync_single_for_cpu(dmadev, cmd->sge_dma, - sizeof(cmd->scatter_gather_element), - DMA_TO_DEVICE); - - for_each_sg(sg, sg, n, i) { - pt[i].high = cpu_to_be32(sg_dma_len(sg) << 16); - pt[i].low = cpu_to_be32(sg_dma_address(sg)); - } - - orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1) | - ORB_SET_DATA_SIZE(n); - orb->data_descriptor_lo = cmd->sge_dma; - - dma_sync_single_for_device(dmadev, cmd->sge_dma, - sizeof(cmd->scatter_gather_element), - DMA_TO_DEVICE); - } - return 0; -} - -static int sbp2_create_command_orb(struct sbp2_lu *lu, - struct sbp2_command_info *cmd, - struct scsi_cmnd *SCpnt) -{ - struct device *dmadev = lu->hi->host->device.parent; - struct sbp2_command_orb *orb = &cmd->command_orb; - unsigned int scsi_request_bufflen = scsi_bufflen(SCpnt); - enum dma_data_direction dma_dir = SCpnt->sc_data_direction; - u32 orb_direction; - int ret; - - dma_sync_single_for_cpu(dmadev, cmd->command_orb_dma, - sizeof(struct sbp2_command_orb), DMA_TO_DEVICE); - /* - * Set-up our command ORB. - * - * NOTE: We're doing unrestricted page tables (s/g), as this is - * best performance (at least with the devices I have). This means - * that data_size becomes the number of s/g elements, and - * page_size should be zero (for unrestricted). - */ - orb->next_ORB_hi = ORB_SET_NULL_PTR(1); - orb->next_ORB_lo = 0x0; - orb->misc = ORB_SET_MAX_PAYLOAD(lu->max_payload_size); - orb->misc |= ORB_SET_SPEED(lu->speed_code); - orb->misc |= ORB_SET_NOTIFY(1); - - if (dma_dir == DMA_NONE) - orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER; - else if (dma_dir == DMA_TO_DEVICE && scsi_request_bufflen) - orb_direction = ORB_DIRECTION_WRITE_TO_MEDIA; - else if (dma_dir == DMA_FROM_DEVICE && scsi_request_bufflen) - orb_direction = ORB_DIRECTION_READ_FROM_MEDIA; - else { - SBP2_INFO("Falling back to DMA_NONE"); - orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER; - } - - /* set up our page table stuff */ - if (orb_direction == ORB_DIRECTION_NO_DATA_TRANSFER) { - orb->data_descriptor_hi = 0x0; - orb->data_descriptor_lo = 0x0; - orb->misc |= ORB_SET_DIRECTION(1); - ret = 0; - } else { - ret = sbp2_prep_command_orb_sg(orb, lu->hi, cmd, - scsi_sg_count(SCpnt), - scsi_sglist(SCpnt), - orb_direction, dma_dir); - } - sbp2util_cpu_to_be32_buffer(orb, sizeof(*orb)); - - memset(orb->cdb, 0, sizeof(orb->cdb)); - memcpy(orb->cdb, SCpnt->cmnd, SCpnt->cmd_len); - - dma_sync_single_for_device(dmadev, cmd->command_orb_dma, - sizeof(struct sbp2_command_orb), DMA_TO_DEVICE); - return ret; -} - -static void sbp2_link_orb_command(struct sbp2_lu *lu, - struct sbp2_command_info *cmd) -{ - struct sbp2_fwhost_info *hi = lu->hi; - struct sbp2_command_orb *last_orb; - dma_addr_t last_orb_dma; - u64 addr = lu->command_block_agent_addr; - quadlet_t data[2]; - size_t length; - unsigned long flags; - - /* check to see if there are any previous orbs to use */ - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - last_orb = lu->last_orb; - last_orb_dma = lu->last_orb_dma; - if (!last_orb) { - /* - * last_orb == NULL means: We know that the target's fetch agent - * is not active right now. - */ - addr += SBP2_ORB_POINTER_OFFSET; - data[0] = ORB_SET_NODE_ID(hi->host->node_id); - data[1] = cmd->command_orb_dma; - sbp2util_cpu_to_be32_buffer(data, 8); - length = 8; - } else { - /* - * last_orb != NULL means: We know that the target's fetch agent - * is (very probably) not dead or in reset state right now. - * We have an ORB already sent that we can append a new one to. - * The target's fetch agent may or may not have read this - * previous ORB yet. - */ - dma_sync_single_for_cpu(hi->host->device.parent, last_orb_dma, - sizeof(struct sbp2_command_orb), - DMA_TO_DEVICE); - last_orb->next_ORB_lo = cpu_to_be32(cmd->command_orb_dma); - wmb(); - /* Tells hardware that this pointer is valid */ - last_orb->next_ORB_hi = 0; - dma_sync_single_for_device(hi->host->device.parent, - last_orb_dma, - sizeof(struct sbp2_command_orb), - DMA_TO_DEVICE); - addr += SBP2_DOORBELL_OFFSET; - data[0] = 0; - length = 4; - } - lu->last_orb = &cmd->command_orb; - lu->last_orb_dma = cmd->command_orb_dma; - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - - if (sbp2util_node_write_no_wait(lu->ne, addr, data, length)) { - /* - * sbp2util_node_write_no_wait failed. We certainly ran out - * of transaction labels, perhaps just because there were no - * context switches which gave khpsbpkt a chance to collect - * free tlabels. Try again in non-atomic context. If necessary, - * the workqueue job will sleep to guaranteedly get a tlabel. - * We do not accept new commands until the job is over. - */ - scsi_block_requests(lu->shost); - PREPARE_WORK(&lu->protocol_work, - last_orb ? sbp2util_write_doorbell: - sbp2util_write_orb_pointer); - schedule_work(&lu->protocol_work); - } -} - -static int sbp2_send_command(struct sbp2_lu *lu, struct scsi_cmnd *SCpnt, - void (*done)(struct scsi_cmnd *)) -{ - struct sbp2_command_info *cmd; - - cmd = sbp2util_allocate_command_orb(lu, SCpnt, done); - if (!cmd) - return -EIO; - - if (sbp2_create_command_orb(lu, cmd, SCpnt)) - return -ENOMEM; - - sbp2_link_orb_command(lu, cmd); - return 0; -} - -/* - * Translates SBP-2 status into SCSI sense data for check conditions - */ -static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status, - unchar *sense_data) -{ - /* OK, it's pretty ugly... ;-) */ - sense_data[0] = 0x70; - sense_data[1] = 0x0; - sense_data[2] = sbp2_status[9]; - sense_data[3] = sbp2_status[12]; - sense_data[4] = sbp2_status[13]; - sense_data[5] = sbp2_status[14]; - sense_data[6] = sbp2_status[15]; - sense_data[7] = 10; - sense_data[8] = sbp2_status[16]; - sense_data[9] = sbp2_status[17]; - sense_data[10] = sbp2_status[18]; - sense_data[11] = sbp2_status[19]; - sense_data[12] = sbp2_status[10]; - sense_data[13] = sbp2_status[11]; - sense_data[14] = sbp2_status[20]; - sense_data[15] = sbp2_status[21]; - - return sbp2_status[8] & 0x3f; -} - -static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, - int destid, quadlet_t *data, u64 addr, - size_t length, u16 fl) -{ - struct sbp2_fwhost_info *hi; - struct sbp2_lu *lu = NULL, *lu_tmp; - struct scsi_cmnd *SCpnt = NULL; - struct sbp2_status_block *sb; - u32 scsi_status = SBP2_SCSI_STATUS_GOOD; - struct sbp2_command_info *cmd; - unsigned long flags; - - if (unlikely(length < 8 || length > sizeof(struct sbp2_status_block))) { - SBP2_ERR("Wrong size of status block"); - return RCODE_ADDRESS_ERROR; - } - if (unlikely(!host)) { - SBP2_ERR("host is NULL - this is bad!"); - return RCODE_ADDRESS_ERROR; - } - hi = hpsb_get_hostinfo(&sbp2_highlevel, host); - if (unlikely(!hi)) { - SBP2_ERR("host info is NULL - this is bad!"); - return RCODE_ADDRESS_ERROR; - } - - /* Find the unit which wrote the status. */ - read_lock_irqsave(&sbp2_hi_logical_units_lock, flags); - list_for_each_entry(lu_tmp, &hi->logical_units, lu_list) { - if (lu_tmp->ne->nodeid == nodeid && - lu_tmp->status_fifo_addr == addr) { - lu = lu_tmp; - break; - } - } - read_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); - - if (unlikely(!lu)) { - SBP2_ERR("lu is NULL - device is gone?"); - return RCODE_ADDRESS_ERROR; - } - - /* Put response into lu status fifo buffer. The first two bytes - * come in big endian bit order. Often the target writes only a - * truncated status block, minimally the first two quadlets. The rest - * is implied to be zeros. */ - sb = &lu->status_block; - memset(sb->command_set_dependent, 0, sizeof(sb->command_set_dependent)); - memcpy(sb, data, length); - sbp2util_be32_to_cpu_buffer(sb, 8); - - /* Ignore unsolicited status. Handle command ORB status. */ - if (unlikely(STATUS_GET_SRC(sb->ORB_offset_hi_misc) == 2)) - cmd = NULL; - else - cmd = sbp2util_find_command_for_orb(lu, sb->ORB_offset_lo); - if (cmd) { - /* Grab SCSI command pointers and check status. */ - /* - * FIXME: If the src field in the status is 1, the ORB DMA must - * not be reused until status for a subsequent ORB is received. - */ - SCpnt = cmd->Current_SCpnt; - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - sbp2util_mark_command_completed(lu, cmd); - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - - if (SCpnt) { - u32 h = sb->ORB_offset_hi_misc; - u32 r = STATUS_GET_RESP(h); - - if (r != RESP_STATUS_REQUEST_COMPLETE) { - SBP2_INFO("resp 0x%x, sbp_status 0x%x", - r, STATUS_GET_SBP_STATUS(h)); - scsi_status = - r == RESP_STATUS_TRANSPORT_FAILURE ? - SBP2_SCSI_STATUS_BUSY : - SBP2_SCSI_STATUS_COMMAND_TERMINATED; - } - - if (STATUS_GET_LEN(h) > 1) - scsi_status = sbp2_status_to_sense_data( - (unchar *)sb, SCpnt->sense_buffer); - - if (STATUS_TEST_DEAD(h)) - sbp2_agent_reset(lu, 0); - } - - /* Check here to see if there are no commands in-use. If there - * are none, we know that the fetch agent left the active state - * _and_ that we did not reactivate it yet. Therefore clear - * last_orb so that next time we write directly to the - * ORB_POINTER register. That way the fetch agent does not need - * to refetch the next_ORB. */ - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - if (list_empty(&lu->cmd_orb_inuse)) - lu->last_orb = NULL; - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - - } else { - /* It's probably status after a management request. */ - if ((sb->ORB_offset_lo == lu->reconnect_orb_dma) || - (sb->ORB_offset_lo == lu->login_orb_dma) || - (sb->ORB_offset_lo == lu->query_logins_orb_dma) || - (sb->ORB_offset_lo == lu->logout_orb_dma)) { - lu->access_complete = 1; - wake_up_interruptible(&sbp2_access_wq); - } - } - - if (SCpnt) - sbp2scsi_complete_command(lu, scsi_status, SCpnt, - cmd->Current_done); - return RCODE_COMPLETE; -} - -/************************************** - * SCSI interface related section - **************************************/ - -static int sbp2scsi_queuecommand(struct scsi_cmnd *SCpnt, - void (*done)(struct scsi_cmnd *)) -{ - struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0]; - struct sbp2_fwhost_info *hi; - int result = DID_NO_CONNECT << 16; - - if (unlikely(!sbp2util_node_is_available(lu))) - goto done; - - hi = lu->hi; - - if (unlikely(!hi)) { - SBP2_ERR("sbp2_fwhost_info is NULL - this is bad!"); - goto done; - } - - /* Multiple units are currently represented to the SCSI core as separate - * targets, not as one target with multiple LUs. Therefore return - * selection time-out to any IO directed at non-zero LUNs. */ - if (unlikely(SCpnt->device->lun)) - goto done; - - if (unlikely(!hpsb_node_entry_valid(lu->ne))) { - SBP2_ERR("Bus reset in progress - rejecting command"); - result = DID_BUS_BUSY << 16; - goto done; - } - - /* Bidirectional commands are not yet implemented, - * and unknown transfer direction not handled. */ - if (unlikely(SCpnt->sc_data_direction == DMA_BIDIRECTIONAL)) { - SBP2_ERR("Cannot handle DMA_BIDIRECTIONAL - rejecting command"); - result = DID_ERROR << 16; - goto done; - } - - if (sbp2_send_command(lu, SCpnt, done)) { - SBP2_ERR("Error sending SCSI command"); - sbp2scsi_complete_command(lu, - SBP2_SCSI_STATUS_SELECTION_TIMEOUT, - SCpnt, done); - } - return 0; - -done: - SCpnt->result = result; - done(SCpnt); - return 0; -} - -static void sbp2scsi_complete_all_commands(struct sbp2_lu *lu, u32 status) -{ - struct list_head *lh; - struct sbp2_command_info *cmd; - unsigned long flags; - - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - while (!list_empty(&lu->cmd_orb_inuse)) { - lh = lu->cmd_orb_inuse.next; - cmd = list_entry(lh, struct sbp2_command_info, list); - sbp2util_mark_command_completed(lu, cmd); - if (cmd->Current_SCpnt) { - cmd->Current_SCpnt->result = status << 16; - cmd->Current_done(cmd->Current_SCpnt); - } - } - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - - return; -} - -/* - * Complete a regular SCSI command. Can be called in atomic context. - */ -static void sbp2scsi_complete_command(struct sbp2_lu *lu, u32 scsi_status, - struct scsi_cmnd *SCpnt, - void (*done)(struct scsi_cmnd *)) -{ - if (!SCpnt) { - SBP2_ERR("SCpnt is NULL"); - return; - } - - switch (scsi_status) { - case SBP2_SCSI_STATUS_GOOD: - SCpnt->result = DID_OK << 16; - break; - - case SBP2_SCSI_STATUS_BUSY: - SBP2_ERR("SBP2_SCSI_STATUS_BUSY"); - SCpnt->result = DID_BUS_BUSY << 16; - break; - - case SBP2_SCSI_STATUS_CHECK_CONDITION: - SCpnt->result = CHECK_CONDITION << 1 | DID_OK << 16; - break; - - case SBP2_SCSI_STATUS_SELECTION_TIMEOUT: - SBP2_ERR("SBP2_SCSI_STATUS_SELECTION_TIMEOUT"); - SCpnt->result = DID_NO_CONNECT << 16; - scsi_print_command(SCpnt); - break; - - case SBP2_SCSI_STATUS_CONDITION_MET: - case SBP2_SCSI_STATUS_RESERVATION_CONFLICT: - case SBP2_SCSI_STATUS_COMMAND_TERMINATED: - SBP2_ERR("Bad SCSI status = %x", scsi_status); - SCpnt->result = DID_ERROR << 16; - scsi_print_command(SCpnt); - break; - - default: - SBP2_ERR("Unsupported SCSI status = %x", scsi_status); - SCpnt->result = DID_ERROR << 16; - } - - /* If a bus reset is in progress and there was an error, complete - * the command as busy so that it will get retried. */ - if (!hpsb_node_entry_valid(lu->ne) - && (scsi_status != SBP2_SCSI_STATUS_GOOD)) { - SBP2_ERR("Completing command with busy (bus reset)"); - SCpnt->result = DID_BUS_BUSY << 16; - } - - /* Tell the SCSI stack that we're done with this command. */ - done(SCpnt); -} - -static int sbp2scsi_slave_alloc(struct scsi_device *sdev) -{ - struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0]; - - if (sdev->lun != 0 || sdev->id != lu->ud->id || sdev->channel != 0) - return -ENODEV; - - lu->sdev = sdev; - sdev->allow_restart = 1; - - /* SBP-2 requires quadlet alignment of the data buffers. */ - blk_queue_update_dma_alignment(sdev->request_queue, 4 - 1); - - if (lu->workarounds & SBP2_WORKAROUND_INQUIRY_36) - sdev->inquiry_len = 36; - return 0; -} - -static int sbp2scsi_slave_configure(struct scsi_device *sdev) -{ - struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0]; - - sdev->use_10_for_rw = 1; - - if (sbp2_exclusive_login) - sdev->manage_start_stop = 1; - if (sdev->type == TYPE_ROM) - sdev->use_10_for_ms = 1; - if (sdev->type == TYPE_DISK && - lu->workarounds & SBP2_WORKAROUND_MODE_SENSE_8) - sdev->skip_ms_page_8 = 1; - if (lu->workarounds & SBP2_WORKAROUND_FIX_CAPACITY) - sdev->fix_capacity = 1; - if (lu->workarounds & SBP2_WORKAROUND_POWER_CONDITION) - sdev->start_stop_pwr_cond = 1; - if (lu->workarounds & SBP2_WORKAROUND_128K_MAX_TRANS) - blk_queue_max_hw_sectors(sdev->request_queue, 128 * 1024 / 512); - - blk_queue_max_segment_size(sdev->request_queue, SBP2_MAX_SEG_SIZE); - return 0; -} - -static void sbp2scsi_slave_destroy(struct scsi_device *sdev) -{ - ((struct sbp2_lu *)sdev->host->hostdata[0])->sdev = NULL; - return; -} - -/* - * Called by scsi stack when something has really gone wrong. - * Usually called when a command has timed-out for some reason. - */ -static int sbp2scsi_abort(struct scsi_cmnd *SCpnt) -{ - struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0]; - struct sbp2_command_info *cmd; - unsigned long flags; - - SBP2_INFO("aborting sbp2 command"); - scsi_print_command(SCpnt); - - if (sbp2util_node_is_available(lu)) { - sbp2_agent_reset(lu, 1); - - /* Return a matching command structure to the free pool. */ - spin_lock_irqsave(&lu->cmd_orb_lock, flags); - cmd = sbp2util_find_command_for_SCpnt(lu, SCpnt); - if (cmd) { - sbp2util_mark_command_completed(lu, cmd); - if (cmd->Current_SCpnt) { - cmd->Current_SCpnt->result = DID_ABORT << 16; - cmd->Current_done(cmd->Current_SCpnt); - } - } - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); - - sbp2scsi_complete_all_commands(lu, DID_BUS_BUSY); - } - - return SUCCESS; -} - -/* - * Called by scsi stack when something has really gone wrong. - */ -static int sbp2scsi_reset(struct scsi_cmnd *SCpnt) -{ - struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0]; - - SBP2_INFO("reset requested"); - - if (sbp2util_node_is_available(lu)) { - SBP2_INFO("generating sbp2 fetch agent reset"); - sbp2_agent_reset(lu, 1); - } - - return SUCCESS; -} - -static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct scsi_device *sdev; - struct sbp2_lu *lu; - - if (!(sdev = to_scsi_device(dev))) - return 0; - - if (!(lu = (struct sbp2_lu *)sdev->host->hostdata[0])) - return 0; - - if (sbp2_long_sysfs_ieee1394_id) - return sprintf(buf, "%016Lx:%06x:%04x\n", - (unsigned long long)lu->ne->guid, - lu->ud->directory_id, ORB_SET_LUN(lu->lun)); - else - return sprintf(buf, "%016Lx:%d:%d\n", - (unsigned long long)lu->ne->guid, - lu->ud->id, ORB_SET_LUN(lu->lun)); -} - -MODULE_AUTHOR("Ben Collins "); -MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver"); -MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME); -MODULE_LICENSE("GPL"); - -static int sbp2_module_init(void) -{ - int ret; - - if (sbp2_serialize_io) { - sbp2_shost_template.can_queue = 1; - sbp2_shost_template.cmd_per_lun = 1; - } - - sbp2_shost_template.max_sectors = sbp2_max_sectors; - - hpsb_register_highlevel(&sbp2_highlevel); - ret = hpsb_register_protocol(&sbp2_driver); - if (ret) { - SBP2_ERR("Failed to register protocol"); - hpsb_unregister_highlevel(&sbp2_highlevel); - return ret; - } - return 0; -} - -static void __exit sbp2_module_exit(void) -{ - hpsb_unregister_protocol(&sbp2_driver); - hpsb_unregister_highlevel(&sbp2_highlevel); -} - -module_init(sbp2_module_init); -module_exit(sbp2_module_exit); diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h deleted file mode 100644 index 64a3a66a8a39..000000000000 --- a/drivers/ieee1394/sbp2.h +++ /dev/null @@ -1,346 +0,0 @@ -/* - * sbp2.h - Defines and prototypes for sbp2.c - * - * Copyright (C) 2000 James Goodwin, Filanet Corporation (www.filanet.com) - * jamesg@filanet.com - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef SBP2_H -#define SBP2_H - -#define SBP2_DEVICE_NAME "sbp2" - -/* - * There is no transport protocol limit to the CDB length, but we implement - * a fixed length only. 16 bytes is enough for disks larger than 2 TB. - */ -#define SBP2_MAX_CDB_SIZE 16 - -/* - * SBP-2 specific definitions - */ - -#define ORB_DIRECTION_WRITE_TO_MEDIA 0x0 -#define ORB_DIRECTION_READ_FROM_MEDIA 0x1 -#define ORB_DIRECTION_NO_DATA_TRANSFER 0x2 - -#define ORB_SET_NULL_PTR(v) (((v) & 0x1) << 31) -#define ORB_SET_NOTIFY(v) (((v) & 0x1) << 31) -#define ORB_SET_RQ_FMT(v) (((v) & 0x3) << 29) -#define ORB_SET_NODE_ID(v) (((v) & 0xffff) << 16) -#define ORB_SET_STATUS_FIFO_HI(v, id) ((v) >> 32 | ORB_SET_NODE_ID(id)) -#define ORB_SET_STATUS_FIFO_LO(v) ((v) & 0xffffffff) -#define ORB_SET_DATA_SIZE(v) ((v) & 0xffff) -#define ORB_SET_PAGE_SIZE(v) (((v) & 0x7) << 16) -#define ORB_SET_PAGE_TABLE_PRESENT(v) (((v) & 0x1) << 19) -#define ORB_SET_MAX_PAYLOAD(v) (((v) & 0xf) << 20) -#define ORB_SET_SPEED(v) (((v) & 0x7) << 24) -#define ORB_SET_DIRECTION(v) (((v) & 0x1) << 27) - -struct sbp2_command_orb { - u32 next_ORB_hi; - u32 next_ORB_lo; - u32 data_descriptor_hi; - u32 data_descriptor_lo; - u32 misc; - u8 cdb[SBP2_MAX_CDB_SIZE]; -} __attribute__((packed)); - -#define SBP2_LOGIN_REQUEST 0x0 -#define SBP2_QUERY_LOGINS_REQUEST 0x1 -#define SBP2_RECONNECT_REQUEST 0x3 -#define SBP2_SET_PASSWORD_REQUEST 0x4 -#define SBP2_LOGOUT_REQUEST 0x7 -#define SBP2_ABORT_TASK_REQUEST 0xb -#define SBP2_ABORT_TASK_SET 0xc -#define SBP2_LOGICAL_UNIT_RESET 0xe -#define SBP2_TARGET_RESET_REQUEST 0xf - -#define ORB_SET_LUN(v) ((v) & 0xffff) -#define ORB_SET_FUNCTION(v) (((v) & 0xf) << 16) -#define ORB_SET_RECONNECT(v) (((v) & 0xf) << 20) -#define ORB_SET_EXCLUSIVE(v) ((v) ? 1 << 28 : 0) -#define ORB_SET_LOGIN_RESP_LENGTH(v) ((v) & 0xffff) -#define ORB_SET_PASSWD_LENGTH(v) (((v) & 0xffff) << 16) - -struct sbp2_login_orb { - u32 password_hi; - u32 password_lo; - u32 login_response_hi; - u32 login_response_lo; - u32 lun_misc; - u32 passwd_resp_lengths; - u32 status_fifo_hi; - u32 status_fifo_lo; -} __attribute__((packed)); - -#define RESPONSE_GET_LOGIN_ID(v) ((v) & 0xffff) -#define RESPONSE_GET_LENGTH(v) (((v) >> 16) & 0xffff) -#define RESPONSE_GET_RECONNECT_HOLD(v) ((v) & 0xffff) - -struct sbp2_login_response { - u32 length_login_ID; - u32 command_block_agent_hi; - u32 command_block_agent_lo; - u32 reconnect_hold; -} __attribute__((packed)); - -#define ORB_SET_LOGIN_ID(v) ((v) & 0xffff) -#define ORB_SET_QUERY_LOGINS_RESP_LENGTH(v) ((v) & 0xffff) - -struct sbp2_query_logins_orb { - u32 reserved1; - u32 reserved2; - u32 query_response_hi; - u32 query_response_lo; - u32 lun_misc; - u32 reserved_resp_length; - u32 status_fifo_hi; - u32 status_fifo_lo; -} __attribute__((packed)); - -#define RESPONSE_GET_MAX_LOGINS(v) ((v) & 0xffff) -#define RESPONSE_GET_ACTIVE_LOGINS(v) ((RESPONSE_GET_LENGTH((v)) - 4) / 12) - -struct sbp2_query_logins_response { - u32 length_max_logins; - u32 misc_IDs; - u32 initiator_misc_hi; - u32 initiator_misc_lo; -} __attribute__((packed)); - -struct sbp2_reconnect_orb { - u32 reserved1; - u32 reserved2; - u32 reserved3; - u32 reserved4; - u32 login_ID_misc; - u32 reserved5; - u32 status_fifo_hi; - u32 status_fifo_lo; -} __attribute__((packed)); - -struct sbp2_logout_orb { - u32 reserved1; - u32 reserved2; - u32 reserved3; - u32 reserved4; - u32 login_ID_misc; - u32 reserved5; - u32 status_fifo_hi; - u32 status_fifo_lo; -} __attribute__((packed)); - -struct sbp2_unrestricted_page_table { - __be32 high; - __be32 low; -}; - -#define RESP_STATUS_REQUEST_COMPLETE 0x0 -#define RESP_STATUS_TRANSPORT_FAILURE 0x1 -#define RESP_STATUS_ILLEGAL_REQUEST 0x2 -#define RESP_STATUS_VENDOR_DEPENDENT 0x3 - -#define SBP2_STATUS_NO_ADDITIONAL_INFO 0x0 -#define SBP2_STATUS_REQ_TYPE_NOT_SUPPORTED 0x1 -#define SBP2_STATUS_SPEED_NOT_SUPPORTED 0x2 -#define SBP2_STATUS_PAGE_SIZE_NOT_SUPPORTED 0x3 -#define SBP2_STATUS_ACCESS_DENIED 0x4 -#define SBP2_STATUS_LU_NOT_SUPPORTED 0x5 -#define SBP2_STATUS_MAX_PAYLOAD_TOO_SMALL 0x6 -#define SBP2_STATUS_RESOURCES_UNAVAILABLE 0x8 -#define SBP2_STATUS_FUNCTION_REJECTED 0x9 -#define SBP2_STATUS_LOGIN_ID_NOT_RECOGNIZED 0xa -#define SBP2_STATUS_DUMMY_ORB_COMPLETED 0xb -#define SBP2_STATUS_REQUEST_ABORTED 0xc -#define SBP2_STATUS_UNSPECIFIED_ERROR 0xff - -#define SFMT_CURRENT_ERROR 0x0 -#define SFMT_DEFERRED_ERROR 0x1 -#define SFMT_VENDOR_DEPENDENT_STATUS 0x3 - -#define STATUS_GET_SRC(v) (((v) >> 30) & 0x3) -#define STATUS_GET_RESP(v) (((v) >> 28) & 0x3) -#define STATUS_GET_LEN(v) (((v) >> 24) & 0x7) -#define STATUS_GET_SBP_STATUS(v) (((v) >> 16) & 0xff) -#define STATUS_GET_ORB_OFFSET_HI(v) ((v) & 0x0000ffff) -#define STATUS_TEST_DEAD(v) ((v) & 0x08000000) -/* test 'resp' | 'dead' | 'sbp2_status' */ -#define STATUS_TEST_RDS(v) ((v) & 0x38ff0000) - -struct sbp2_status_block { - u32 ORB_offset_hi_misc; - u32 ORB_offset_lo; - u8 command_set_dependent[24]; -} __attribute__((packed)); - - -/* - * SBP2 related configuration ROM definitions - */ - -#define SBP2_UNIT_DIRECTORY_OFFSET_KEY 0xd1 -#define SBP2_CSR_OFFSET_KEY 0x54 -#define SBP2_UNIT_SPEC_ID_KEY 0x12 -#define SBP2_UNIT_SW_VERSION_KEY 0x13 -#define SBP2_COMMAND_SET_SPEC_ID_KEY 0x38 -#define SBP2_COMMAND_SET_KEY 0x39 -#define SBP2_UNIT_CHARACTERISTICS_KEY 0x3a -#define SBP2_DEVICE_TYPE_AND_LUN_KEY 0x14 -#define SBP2_FIRMWARE_REVISION_KEY 0x3c - -#define SBP2_AGENT_STATE_OFFSET 0x00ULL -#define SBP2_AGENT_RESET_OFFSET 0x04ULL -#define SBP2_ORB_POINTER_OFFSET 0x08ULL -#define SBP2_DOORBELL_OFFSET 0x10ULL -#define SBP2_UNSOLICITED_STATUS_ENABLE_OFFSET 0x14ULL -#define SBP2_UNSOLICITED_STATUS_VALUE 0xf - -#define SBP2_BUSY_TIMEOUT_ADDRESS 0xfffff0000210ULL -/* biggest possible value for Single Phase Retry count is 0xf */ -#define SBP2_BUSY_TIMEOUT_VALUE 0xf - -#define SBP2_AGENT_RESET_DATA 0xf - -#define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e -#define SBP2_SW_VERSION_ENTRY 0x00010483 - -/* - * The default maximum s/g segment size of a FireWire controller is - * usually 0x10000, but SBP-2 only allows 0xffff. Since buffers have to - * be quadlet-aligned, we set the length limit to 0xffff & ~3. - */ -#define SBP2_MAX_SEG_SIZE 0xfffc - -/* - * There is no real limitation of the queue depth (i.e. length of the linked - * list of command ORBs) at the target. The chosen depth is merely an - * implementation detail of the sbp2 driver. - */ -#define SBP2_MAX_CMDS 8 - -#define SBP2_SCSI_STATUS_GOOD 0x0 -#define SBP2_SCSI_STATUS_CHECK_CONDITION 0x2 -#define SBP2_SCSI_STATUS_CONDITION_MET 0x4 -#define SBP2_SCSI_STATUS_BUSY 0x8 -#define SBP2_SCSI_STATUS_RESERVATION_CONFLICT 0x18 -#define SBP2_SCSI_STATUS_COMMAND_TERMINATED 0x22 -#define SBP2_SCSI_STATUS_SELECTION_TIMEOUT 0xff - - -/* - * Representations of commands and devices - */ - -/* Per SCSI command */ -struct sbp2_command_info { - struct list_head list; - struct sbp2_command_orb command_orb; - dma_addr_t command_orb_dma; - struct scsi_cmnd *Current_SCpnt; - void (*Current_done)(struct scsi_cmnd *); - - /* Also need s/g structure for each sbp2 command */ - struct sbp2_unrestricted_page_table - scatter_gather_element[SG_ALL] __attribute__((aligned(8))); - dma_addr_t sge_dma; -}; - -/* Per FireWire host */ -struct sbp2_fwhost_info { - struct hpsb_host *host; - struct list_head logical_units; -}; - -/* Per logical unit */ -struct sbp2_lu { - /* Operation request blocks */ - struct sbp2_command_orb *last_orb; - dma_addr_t last_orb_dma; - struct sbp2_login_orb *login_orb; - dma_addr_t login_orb_dma; - struct sbp2_login_response *login_response; - dma_addr_t login_response_dma; - struct sbp2_query_logins_orb *query_logins_orb; - dma_addr_t query_logins_orb_dma; - struct sbp2_query_logins_response *query_logins_response; - dma_addr_t query_logins_response_dma; - struct sbp2_reconnect_orb *reconnect_orb; - dma_addr_t reconnect_orb_dma; - struct sbp2_logout_orb *logout_orb; - dma_addr_t logout_orb_dma; - struct sbp2_status_block status_block; - - /* How to talk to the unit */ - u64 management_agent_addr; - u64 command_block_agent_addr; - u32 speed_code; - u32 max_payload_size; - u16 lun; - - /* Address for the unit to write status blocks to */ - u64 status_fifo_addr; - - /* Waitqueue flag for logins, reconnects, logouts, query logins */ - unsigned int access_complete:1; - - /* Pool of command ORBs for this logical unit */ - spinlock_t cmd_orb_lock; - struct list_head cmd_orb_inuse; - struct list_head cmd_orb_completed; - - /* Backlink to FireWire host; list of units attached to the host */ - struct sbp2_fwhost_info *hi; - struct list_head lu_list; - - /* IEEE 1394 core's device representations */ - struct node_entry *ne; - struct unit_directory *ud; - - /* SCSI core's device representations */ - struct scsi_device *sdev; - struct Scsi_Host *shost; - - /* Device specific workarounds/brokeness */ - unsigned workarounds; - - /* Connection state */ - atomic_t state; - - /* For deferred requests to the fetch agent */ - struct work_struct protocol_work; -}; - -/* For use in sbp2_lu.state */ -enum sbp2lu_state_types { - SBP2LU_STATE_RUNNING, /* all normal */ - SBP2LU_STATE_IN_RESET, /* between bus reset and reconnect */ - SBP2LU_STATE_IN_SHUTDOWN /* when sbp2_remove was called */ -}; - -/* For use in sbp2_lu.workarounds and in the corresponding - * module load parameter */ -#define SBP2_WORKAROUND_128K_MAX_TRANS 0x1 -#define SBP2_WORKAROUND_INQUIRY_36 0x2 -#define SBP2_WORKAROUND_MODE_SENSE_8 0x4 -#define SBP2_WORKAROUND_FIX_CAPACITY 0x8 -#define SBP2_WORKAROUND_DELAY_INQUIRY 0x10 -#define SBP2_INQUIRY_DELAY 12 -#define SBP2_WORKAROUND_POWER_CONDITION 0x20 -#define SBP2_WORKAROUND_OVERRIDE 0x100 - -#endif /* SBP2_H */ diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c deleted file mode 100644 index 5c74f796d7f1..000000000000 --- a/drivers/ieee1394/video1394.c +++ /dev/null @@ -1,1528 +0,0 @@ -/* - * video1394.c - video driver for OHCI 1394 boards - * Copyright (C)1999,2000 Sebastien Rougeaux - * Peter Schlaile - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * NOTES: - * - * ioctl return codes: - * EFAULT is only for invalid address for the argp - * EINVAL for out of range values - * EBUSY when trying to use an already used resource - * ESRCH when trying to free/stop a not used resource - * EAGAIN for resource allocation failure that could perhaps succeed later - * ENOTTY for unsupported ioctl request - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dma.h" -#include "highlevel.h" -#include "hosts.h" -#include "ieee1394.h" -#include "ieee1394_core.h" -#include "ieee1394_hotplug.h" -#include "ieee1394_types.h" -#include "nodemgr.h" -#include "ohci1394.h" -#include "video1394.h" - -#define ISO_CHANNELS 64 - -struct it_dma_prg { - struct dma_cmd begin; - quadlet_t data[4]; - struct dma_cmd end; - quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */ -}; - -struct dma_iso_ctx { - struct ti_ohci *ohci; - int type; /* OHCI_ISO_TRANSMIT or OHCI_ISO_RECEIVE */ - struct ohci1394_iso_tasklet iso_tasklet; - int channel; - int ctx; - int last_buffer; - int * next_buffer; /* For ISO Transmit of video packets - to write the correct SYT field - into the next block */ - unsigned int num_desc; - unsigned int buf_size; - unsigned int frame_size; - unsigned int packet_size; - unsigned int left_size; - unsigned int nb_cmd; - - struct dma_region dma; - - struct dma_prog_region *prg_reg; - - struct dma_cmd **ir_prg; - struct it_dma_prg **it_prg; - - unsigned int *buffer_status; - unsigned int *buffer_prg_assignment; - struct timeval *buffer_time; /* time when the buffer was received */ - unsigned int *last_used_cmd; /* For ISO Transmit with - variable sized packets only ! */ - int ctrlClear; - int ctrlSet; - int cmdPtr; - int ctxMatch; - wait_queue_head_t waitq; - spinlock_t lock; - unsigned int syt_offset; - int flags; - - struct list_head link; -}; - - -struct file_ctx { - struct ti_ohci *ohci; - struct list_head context_list; - struct dma_iso_ctx *current_ctx; -}; - -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG -#define VIDEO1394_DEBUG -#endif - -#ifdef DBGMSG -#undef DBGMSG -#endif - -#ifdef VIDEO1394_DEBUG -#define DBGMSG(card, fmt, args...) \ -printk(KERN_INFO "video1394_%d: " fmt "\n" , card , ## args) -#else -#define DBGMSG(card, fmt, args...) do {} while (0) -#endif - -/* print general (card independent) information */ -#define PRINT_G(level, fmt, args...) \ -printk(level "video1394: " fmt "\n" , ## args) - -/* print card specific information */ -#define PRINT(level, card, fmt, args...) \ -printk(level "video1394_%d: " fmt "\n" , card , ## args) - -static void wakeup_dma_ir_ctx(unsigned long l); -static void wakeup_dma_it_ctx(unsigned long l); - -static struct hpsb_highlevel video1394_highlevel; - -static int free_dma_iso_ctx(struct dma_iso_ctx *d) -{ - int i; - - DBGMSG(d->ohci->host->id, "Freeing dma_iso_ctx %d", d->ctx); - - ohci1394_stop_context(d->ohci, d->ctrlClear, NULL); - if (d->iso_tasklet.link.next != NULL) - ohci1394_unregister_iso_tasklet(d->ohci, &d->iso_tasklet); - - dma_region_free(&d->dma); - - if (d->prg_reg) { - for (i = 0; i < d->num_desc; i++) - dma_prog_region_free(&d->prg_reg[i]); - kfree(d->prg_reg); - } - - kfree(d->ir_prg); - kfree(d->it_prg); - kfree(d->buffer_status); - kfree(d->buffer_prg_assignment); - kfree(d->buffer_time); - kfree(d->last_used_cmd); - kfree(d->next_buffer); - list_del(&d->link); - kfree(d); - - return 0; -} - -static struct dma_iso_ctx * -alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc, - int buf_size, int channel, unsigned int packet_size) -{ - struct dma_iso_ctx *d; - int i; - - d = kzalloc(sizeof(*d), GFP_KERNEL); - if (!d) { - PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma_iso_ctx"); - return NULL; - } - - d->ohci = ohci; - d->type = type; - d->channel = channel; - d->num_desc = num_desc; - d->frame_size = buf_size; - d->buf_size = PAGE_ALIGN(buf_size); - d->last_buffer = -1; - INIT_LIST_HEAD(&d->link); - init_waitqueue_head(&d->waitq); - - /* Init the regions for easy cleanup */ - dma_region_init(&d->dma); - - if (dma_region_alloc(&d->dma, (d->num_desc - 1) * d->buf_size, ohci->dev, - PCI_DMA_BIDIRECTIONAL)) { - PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma buffer"); - free_dma_iso_ctx(d); - return NULL; - } - - if (type == OHCI_ISO_RECEIVE) - ohci1394_init_iso_tasklet(&d->iso_tasklet, type, - wakeup_dma_ir_ctx, - (unsigned long) d); - else - ohci1394_init_iso_tasklet(&d->iso_tasklet, type, - wakeup_dma_it_ctx, - (unsigned long) d); - - if (ohci1394_register_iso_tasklet(ohci, &d->iso_tasklet) < 0) { - PRINT(KERN_ERR, ohci->host->id, "no free iso %s contexts", - type == OHCI_ISO_RECEIVE ? "receive" : "transmit"); - free_dma_iso_ctx(d); - return NULL; - } - d->ctx = d->iso_tasklet.context; - - d->prg_reg = kmalloc(d->num_desc * sizeof(*d->prg_reg), GFP_KERNEL); - if (!d->prg_reg) { - PRINT(KERN_ERR, ohci->host->id, "Failed to allocate ir prg regs"); - free_dma_iso_ctx(d); - return NULL; - } - /* Makes for easier cleanup */ - for (i = 0; i < d->num_desc; i++) - dma_prog_region_init(&d->prg_reg[i]); - - if (type == OHCI_ISO_RECEIVE) { - d->ctrlSet = OHCI1394_IsoRcvContextControlSet+32*d->ctx; - d->ctrlClear = OHCI1394_IsoRcvContextControlClear+32*d->ctx; - d->cmdPtr = OHCI1394_IsoRcvCommandPtr+32*d->ctx; - d->ctxMatch = OHCI1394_IsoRcvContextMatch+32*d->ctx; - - d->ir_prg = kzalloc(d->num_desc * sizeof(*d->ir_prg), - GFP_KERNEL); - - if (!d->ir_prg) { - PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma ir prg"); - free_dma_iso_ctx(d); - return NULL; - } - - d->nb_cmd = d->buf_size / PAGE_SIZE + 1; - d->left_size = (d->frame_size % PAGE_SIZE) ? - d->frame_size % PAGE_SIZE : PAGE_SIZE; - - for (i = 0;i < d->num_desc; i++) { - if (dma_prog_region_alloc(&d->prg_reg[i], d->nb_cmd * - sizeof(struct dma_cmd), ohci->dev)) { - PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma ir prg"); - free_dma_iso_ctx(d); - return NULL; - } - d->ir_prg[i] = (struct dma_cmd *)d->prg_reg[i].kvirt; - } - - } else { /* OHCI_ISO_TRANSMIT */ - d->ctrlSet = OHCI1394_IsoXmitContextControlSet+16*d->ctx; - d->ctrlClear = OHCI1394_IsoXmitContextControlClear+16*d->ctx; - d->cmdPtr = OHCI1394_IsoXmitCommandPtr+16*d->ctx; - - d->it_prg = kzalloc(d->num_desc * sizeof(*d->it_prg), - GFP_KERNEL); - - if (!d->it_prg) { - PRINT(KERN_ERR, ohci->host->id, - "Failed to allocate dma it prg"); - free_dma_iso_ctx(d); - return NULL; - } - - d->packet_size = packet_size; - - if (PAGE_SIZE % packet_size || packet_size>4096) { - PRINT(KERN_ERR, ohci->host->id, - "Packet size %d (page_size: %ld) " - "not yet supported\n", - packet_size, PAGE_SIZE); - free_dma_iso_ctx(d); - return NULL; - } - - d->nb_cmd = d->frame_size / d->packet_size; - if (d->frame_size % d->packet_size) { - d->nb_cmd++; - d->left_size = d->frame_size % d->packet_size; - } else - d->left_size = d->packet_size; - - for (i = 0; i < d->num_desc; i++) { - if (dma_prog_region_alloc(&d->prg_reg[i], d->nb_cmd * - sizeof(struct it_dma_prg), ohci->dev)) { - PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma it prg"); - free_dma_iso_ctx(d); - return NULL; - } - d->it_prg[i] = (struct it_dma_prg *)d->prg_reg[i].kvirt; - } - } - - d->buffer_status = - kzalloc(d->num_desc * sizeof(*d->buffer_status), GFP_KERNEL); - d->buffer_prg_assignment = - kzalloc(d->num_desc * sizeof(*d->buffer_prg_assignment), GFP_KERNEL); - d->buffer_time = - kzalloc(d->num_desc * sizeof(*d->buffer_time), GFP_KERNEL); - d->last_used_cmd = - kzalloc(d->num_desc * sizeof(*d->last_used_cmd), GFP_KERNEL); - d->next_buffer = - kzalloc(d->num_desc * sizeof(*d->next_buffer), GFP_KERNEL); - - if (!d->buffer_status || !d->buffer_prg_assignment || !d->buffer_time || - !d->last_used_cmd || !d->next_buffer) { - PRINT(KERN_ERR, ohci->host->id, - "Failed to allocate dma_iso_ctx member"); - free_dma_iso_ctx(d); - return NULL; - } - - spin_lock_init(&d->lock); - - DBGMSG(ohci->host->id, "Iso %s DMA: %d buffers " - "of size %d allocated for a frame size %d, each with %d prgs", - (type == OHCI_ISO_RECEIVE) ? "receive" : "transmit", - d->num_desc - 1, d->buf_size, d->frame_size, d->nb_cmd); - - return d; -} - -static void reset_ir_status(struct dma_iso_ctx *d, int n) -{ - int i; - d->ir_prg[n][0].status = cpu_to_le32(4); - d->ir_prg[n][1].status = cpu_to_le32(PAGE_SIZE-4); - for (i = 2; i < d->nb_cmd - 1; i++) - d->ir_prg[n][i].status = cpu_to_le32(PAGE_SIZE); - d->ir_prg[n][i].status = cpu_to_le32(d->left_size); -} - -static void reprogram_dma_ir_prg(struct dma_iso_ctx *d, int n, int buffer, int flags) -{ - struct dma_cmd *ir_prg = d->ir_prg[n]; - unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size; - int i; - - d->buffer_prg_assignment[n] = buffer; - - ir_prg[0].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, buf - - (unsigned long)d->dma.kvirt)); - ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf + 4) - (unsigned long)d->dma.kvirt)); - - for (i=2;inb_cmd-1;i++) { - ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf+(i-1)*PAGE_SIZE) - - (unsigned long)d->dma.kvirt)); - } - - ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size); - ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf+(i-1)*PAGE_SIZE) - (unsigned long)d->dma.kvirt)); -} - -static void initialize_dma_ir_prg(struct dma_iso_ctx *d, int n, int flags) -{ - struct dma_cmd *ir_prg = d->ir_prg[n]; - struct dma_prog_region *ir_reg = &d->prg_reg[n]; - unsigned long buf = (unsigned long)d->dma.kvirt; - int i; - - /* the first descriptor will read only 4 bytes */ - ir_prg[0].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_BRANCH | 4); - - /* set the sync flag */ - if (flags & VIDEO1394_SYNC_FRAMES) - ir_prg[0].control |= cpu_to_le32(DMA_CTL_WAIT); - - ir_prg[0].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, buf - - (unsigned long)d->dma.kvirt)); - ir_prg[0].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg, - 1 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1); - - /* If there is *not* only one DMA page per frame (hence, d->nb_cmd==2) */ - if (d->nb_cmd > 2) { - /* The second descriptor will read PAGE_SIZE-4 bytes */ - ir_prg[1].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_BRANCH | (PAGE_SIZE-4)); - ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf + 4) - - (unsigned long)d->dma.kvirt)); - ir_prg[1].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg, - 2 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1); - - for (i = 2; i < d->nb_cmd - 1; i++) { - ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_BRANCH | PAGE_SIZE); - ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf+(i-1)*PAGE_SIZE) - - (unsigned long)d->dma.kvirt)); - - ir_prg[i].branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg, - (i + 1) * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1); - } - - /* The last descriptor will generate an interrupt */ - ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size); - ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf+(i-1)*PAGE_SIZE) - - (unsigned long)d->dma.kvirt)); - } else { - /* Only one DMA page is used. Read d->left_size immediately and */ - /* generate an interrupt as this is also the last page. */ - ir_prg[1].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_IRQ | DMA_CTL_BRANCH | (d->left_size-4)); - ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf + 4) - (unsigned long)d->dma.kvirt)); - } -} - -static void initialize_dma_ir_ctx(struct dma_iso_ctx *d, int tag, int flags) -{ - struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; - int i; - - d->flags = flags; - - ohci1394_stop_context(ohci, d->ctrlClear, NULL); - - for (i=0;inum_desc;i++) { - initialize_dma_ir_prg(d, i, flags); - reset_ir_status(d, i); - } - - /* reset the ctrl register */ - reg_write(ohci, d->ctrlClear, 0xf0000000); - - /* Set bufferFill */ - reg_write(ohci, d->ctrlSet, 0x80000000); - - /* Set isoch header */ - if (flags & VIDEO1394_INCLUDE_ISO_HEADERS) - reg_write(ohci, d->ctrlSet, 0x40000000); - - /* Set the context match register to match on all tags, - sync for sync tag, and listen to d->channel */ - reg_write(ohci, d->ctxMatch, 0xf0000000|((tag&0xf)<<8)|d->channel); - - /* Set up isoRecvIntMask to generate interrupts */ - reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1<ctx); -} - -/* find which context is listening to this channel */ -static struct dma_iso_ctx * -find_ctx(struct list_head *list, int type, int channel) -{ - struct dma_iso_ctx *ctx; - - list_for_each_entry(ctx, list, link) { - if (ctx->type == type && ctx->channel == channel) - return ctx; - } - - return NULL; -} - -static void wakeup_dma_ir_ctx(unsigned long l) -{ - struct dma_iso_ctx *d = (struct dma_iso_ctx *) l; - int i; - - spin_lock(&d->lock); - - for (i = 0; i < d->num_desc; i++) { - if (d->ir_prg[i][d->nb_cmd-1].status & cpu_to_le32(0xFFFF0000)) { - reset_ir_status(d, i); - d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY; - do_gettimeofday(&d->buffer_time[d->buffer_prg_assignment[i]]); - dma_region_sync_for_cpu(&d->dma, - d->buffer_prg_assignment[i] * d->buf_size, - d->buf_size); - } - } - - spin_unlock(&d->lock); - - if (waitqueue_active(&d->waitq)) - wake_up_interruptible(&d->waitq); -} - -static inline void put_timestamp(struct ti_ohci *ohci, struct dma_iso_ctx * d, - int n) -{ - unsigned char* buf = d->dma.kvirt + n * d->buf_size; - u32 cycleTimer; - u32 timeStamp; - - if (n == -1) { - return; - } - - cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - - timeStamp = ((cycleTimer & 0x0fff) + d->syt_offset); /* 11059 = 450 us */ - timeStamp = (timeStamp % 3072 + ((timeStamp / 3072) << 12) - + (cycleTimer & 0xf000)) & 0xffff; - - buf[6] = timeStamp >> 8; - buf[7] = timeStamp & 0xff; - - /* if first packet is empty packet, then put timestamp into the next full one too */ - if ( (le32_to_cpu(d->it_prg[n][0].data[1]) >>16) == 0x008) { - buf += d->packet_size; - buf[6] = timeStamp >> 8; - buf[7] = timeStamp & 0xff; - } - - /* do the next buffer frame too in case of irq latency */ - n = d->next_buffer[n]; - if (n == -1) { - return; - } - buf = d->dma.kvirt + n * d->buf_size; - - timeStamp += (d->last_used_cmd[n] << 12) & 0xffff; - - buf[6] = timeStamp >> 8; - buf[7] = timeStamp & 0xff; - - /* if first packet is empty packet, then put timestamp into the next full one too */ - if ( (le32_to_cpu(d->it_prg[n][0].data[1]) >>16) == 0x008) { - buf += d->packet_size; - buf[6] = timeStamp >> 8; - buf[7] = timeStamp & 0xff; - } - -#if 0 - printk("curr: %d, next: %d, cycleTimer: %08x timeStamp: %08x\n", - curr, n, cycleTimer, timeStamp); -#endif -} - -static void wakeup_dma_it_ctx(unsigned long l) -{ - struct dma_iso_ctx *d = (struct dma_iso_ctx *) l; - struct ti_ohci *ohci = d->ohci; - int i; - - spin_lock(&d->lock); - - for (i = 0; i < d->num_desc; i++) { - if (d->it_prg[i][d->last_used_cmd[i]].end.status & - cpu_to_le32(0xFFFF0000)) { - int next = d->next_buffer[i]; - put_timestamp(ohci, d, next); - d->it_prg[i][d->last_used_cmd[i]].end.status = 0; - d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY; - } - } - - spin_unlock(&d->lock); - - if (waitqueue_active(&d->waitq)) - wake_up_interruptible(&d->waitq); -} - -static void reprogram_dma_it_prg(struct dma_iso_ctx *d, int n, int buffer) -{ - struct it_dma_prg *it_prg = d->it_prg[n]; - unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size; - int i; - - d->buffer_prg_assignment[n] = buffer; - for (i=0;inb_cmd;i++) { - it_prg[i].end.address = - cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf+i*d->packet_size) - (unsigned long)d->dma.kvirt)); - } -} - -static void initialize_dma_it_prg(struct dma_iso_ctx *d, int n, int sync_tag) -{ - struct it_dma_prg *it_prg = d->it_prg[n]; - struct dma_prog_region *it_reg = &d->prg_reg[n]; - unsigned long buf = (unsigned long)d->dma.kvirt; - int i; - d->last_used_cmd[n] = d->nb_cmd - 1; - for (i=0;inb_cmd;i++) { - - it_prg[i].begin.control = cpu_to_le32(DMA_CTL_OUTPUT_MORE | - DMA_CTL_IMMEDIATE | 8) ; - it_prg[i].begin.address = 0; - - it_prg[i].begin.status = 0; - - it_prg[i].data[0] = cpu_to_le32( - (IEEE1394_SPEED_100 << 16) - | (/* tag */ 1 << 14) - | (d->channel << 8) - | (TCODE_ISO_DATA << 4)); - if (i==0) it_prg[i].data[0] |= cpu_to_le32(sync_tag); - it_prg[i].data[1] = cpu_to_le32(d->packet_size << 16); - it_prg[i].data[2] = 0; - it_prg[i].data[3] = 0; - - it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST | - DMA_CTL_BRANCH); - it_prg[i].end.address = - cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf+i*d->packet_size) - - (unsigned long)d->dma.kvirt)); - - if (inb_cmd-1) { - it_prg[i].end.control |= cpu_to_le32(d->packet_size); - it_prg[i].begin.branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * - sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); - it_prg[i].end.branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * - sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); - } else { - /* the last prg generates an interrupt */ - it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE | - DMA_CTL_IRQ | d->left_size); - /* the last prg doesn't branch */ - it_prg[i].begin.branchAddress = 0; - it_prg[i].end.branchAddress = 0; - } - it_prg[i].end.status = 0; - } -} - -static void initialize_dma_it_prg_var_packet_queue( - struct dma_iso_ctx *d, int n, unsigned int * packet_sizes, - struct ti_ohci *ohci) -{ - struct it_dma_prg *it_prg = d->it_prg[n]; - struct dma_prog_region *it_reg = &d->prg_reg[n]; - int i; - -#if 0 - if (n != -1) { - put_timestamp(ohci, d, n); - } -#endif - d->last_used_cmd[n] = d->nb_cmd - 1; - - for (i = 0; i < d->nb_cmd; i++) { - unsigned int size; - if (packet_sizes[i] > d->packet_size) { - size = d->packet_size; - } else { - size = packet_sizes[i]; - } - it_prg[i].data[1] = cpu_to_le32(size << 16); - it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST | DMA_CTL_BRANCH); - - if (i < d->nb_cmd-1 && packet_sizes[i+1] != 0) { - it_prg[i].end.control |= cpu_to_le32(size); - it_prg[i].begin.branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * - sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); - it_prg[i].end.branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * - sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); - } else { - /* the last prg generates an interrupt */ - it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE | - DMA_CTL_IRQ | size); - /* the last prg doesn't branch */ - it_prg[i].begin.branchAddress = 0; - it_prg[i].end.branchAddress = 0; - d->last_used_cmd[n] = i; - break; - } - } -} - -static void initialize_dma_it_ctx(struct dma_iso_ctx *d, int sync_tag, - unsigned int syt_offset, int flags) -{ - struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; - int i; - - d->flags = flags; - d->syt_offset = (syt_offset == 0 ? 11000 : syt_offset); - - ohci1394_stop_context(ohci, d->ctrlClear, NULL); - - for (i=0;inum_desc;i++) - initialize_dma_it_prg(d, i, sync_tag); - - /* Set up isoRecvIntMask to generate interrupts */ - reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1<ctx); -} - -static inline unsigned video1394_buffer_state(struct dma_iso_ctx *d, - unsigned int buffer) -{ - unsigned long flags; - unsigned int ret; - spin_lock_irqsave(&d->lock, flags); - ret = d->buffer_status[buffer]; - spin_unlock_irqrestore(&d->lock, flags); - return ret; -} - -static long video1394_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct file_ctx *ctx = file->private_data; - struct ti_ohci *ohci = ctx->ohci; - unsigned long flags; - void __user *argp = (void __user *)arg; - - switch(cmd) - { - case VIDEO1394_IOC_LISTEN_CHANNEL: - case VIDEO1394_IOC_TALK_CHANNEL: - { - struct video1394_mmap v; - u64 mask; - struct dma_iso_ctx *d; - int i; - - if (copy_from_user(&v, argp, sizeof(v))) - return -EFAULT; - - /* if channel < 0, find lowest available one */ - if (v.channel < 0) { - mask = (u64)0x1; - for (i=0; ; i++) { - if (i == ISO_CHANNELS) { - PRINT(KERN_ERR, ohci->host->id, - "No free channel found"); - return -EAGAIN; - } - if (!(ohci->ISO_channel_usage & mask)) { - v.channel = i; - PRINT(KERN_INFO, ohci->host->id, "Found free channel %d", i); - break; - } - mask = mask << 1; - } - } else if (v.channel >= ISO_CHANNELS) { - PRINT(KERN_ERR, ohci->host->id, - "Iso channel %d out of bounds", v.channel); - return -EINVAL; - } else { - mask = (u64)0x1<host->id, "mask: %08X%08X usage: %08X%08X\n", - (u32)(mask>>32),(u32)(mask&0xffffffff), - (u32)(ohci->ISO_channel_usage>>32), - (u32)(ohci->ISO_channel_usage&0xffffffff)); - if (ohci->ISO_channel_usage & mask) { - PRINT(KERN_ERR, ohci->host->id, - "Channel %d is already taken", v.channel); - return -EBUSY; - } - - if (v.buf_size == 0 || v.buf_size > VIDEO1394_MAX_SIZE) { - PRINT(KERN_ERR, ohci->host->id, - "Invalid %d length buffer requested",v.buf_size); - return -EINVAL; - } - - if (v.nb_buffers == 0 || v.nb_buffers > VIDEO1394_MAX_SIZE) { - PRINT(KERN_ERR, ohci->host->id, - "Invalid %d buffers requested",v.nb_buffers); - return -EINVAL; - } - - if (v.nb_buffers * v.buf_size > VIDEO1394_MAX_SIZE) { - PRINT(KERN_ERR, ohci->host->id, - "%d buffers of size %d bytes is too big", - v.nb_buffers, v.buf_size); - return -EINVAL; - } - - if (cmd == VIDEO1394_IOC_LISTEN_CHANNEL) { - d = alloc_dma_iso_ctx(ohci, OHCI_ISO_RECEIVE, - v.nb_buffers + 1, v.buf_size, - v.channel, 0); - - if (d == NULL) { - PRINT(KERN_ERR, ohci->host->id, - "Couldn't allocate ir context"); - return -EAGAIN; - } - initialize_dma_ir_ctx(d, v.sync_tag, v.flags); - - ctx->current_ctx = d; - - v.buf_size = d->buf_size; - list_add_tail(&d->link, &ctx->context_list); - - DBGMSG(ohci->host->id, - "iso context %d listen on channel %d", - d->ctx, v.channel); - } - else { - d = alloc_dma_iso_ctx(ohci, OHCI_ISO_TRANSMIT, - v.nb_buffers + 1, v.buf_size, - v.channel, v.packet_size); - - if (d == NULL) { - PRINT(KERN_ERR, ohci->host->id, - "Couldn't allocate it context"); - return -EAGAIN; - } - initialize_dma_it_ctx(d, v.sync_tag, - v.syt_offset, v.flags); - - ctx->current_ctx = d; - - v.buf_size = d->buf_size; - - list_add_tail(&d->link, &ctx->context_list); - - DBGMSG(ohci->host->id, - "Iso context %d talk on channel %d", d->ctx, - v.channel); - } - - if (copy_to_user(argp, &v, sizeof(v))) { - /* FIXME : free allocated dma resources */ - return -EFAULT; - } - - ohci->ISO_channel_usage |= mask; - - return 0; - } - case VIDEO1394_IOC_UNLISTEN_CHANNEL: - case VIDEO1394_IOC_UNTALK_CHANNEL: - { - int channel; - u64 mask; - struct dma_iso_ctx *d; - - if (copy_from_user(&channel, argp, sizeof(int))) - return -EFAULT; - - if (channel < 0 || channel >= ISO_CHANNELS) { - PRINT(KERN_ERR, ohci->host->id, - "Iso channel %d out of bound", channel); - return -EINVAL; - } - mask = (u64)0x1<ISO_channel_usage & mask)) { - PRINT(KERN_ERR, ohci->host->id, - "Channel %d is not being used", channel); - return -ESRCH; - } - - /* Mark this channel as unused */ - ohci->ISO_channel_usage &= ~mask; - - if (cmd == VIDEO1394_IOC_UNLISTEN_CHANNEL) - d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, channel); - else - d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, channel); - - if (d == NULL) return -ESRCH; - DBGMSG(ohci->host->id, "Iso context %d " - "stop talking on channel %d", d->ctx, channel); - free_dma_iso_ctx(d); - - return 0; - } - case VIDEO1394_IOC_LISTEN_QUEUE_BUFFER: - { - struct video1394_wait v; - struct dma_iso_ctx *d; - int next_prg; - - if (unlikely(copy_from_user(&v, argp, sizeof(v)))) - return -EFAULT; - - d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel); - if (unlikely(d == NULL)) - return -EFAULT; - - if (unlikely(v.buffer >= d->num_desc - 1)) { - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d out of range",v.buffer); - return -EINVAL; - } - - spin_lock_irqsave(&d->lock,flags); - - if (unlikely(d->buffer_status[v.buffer]==VIDEO1394_BUFFER_QUEUED)) { - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d is already used",v.buffer); - spin_unlock_irqrestore(&d->lock,flags); - return -EBUSY; - } - - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; - - next_prg = (d->last_buffer + 1) % d->num_desc; - if (d->last_buffer>=0) - d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0) - & 0xfffffff0) | 0x1); - - d->last_buffer = next_prg; - reprogram_dma_ir_prg(d, d->last_buffer, v.buffer, d->flags); - - d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = 0; - - spin_unlock_irqrestore(&d->lock,flags); - - if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) - { - DBGMSG(ohci->host->id, "Starting iso DMA ctx=%d",d->ctx); - - /* Tell the controller where the first program is */ - reg_write(ohci, d->cmdPtr, - dma_prog_region_offset_to_bus(&d->prg_reg[d->last_buffer], 0) | 0x1); - - /* Run IR context */ - reg_write(ohci, d->ctrlSet, 0x8000); - } - else { - /* Wake up dma context if necessary */ - if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { - DBGMSG(ohci->host->id, - "Waking up iso dma ctx=%d", d->ctx); - reg_write(ohci, d->ctrlSet, 0x1000); - } - } - return 0; - - } - case VIDEO1394_IOC_LISTEN_WAIT_BUFFER: - case VIDEO1394_IOC_LISTEN_POLL_BUFFER: - { - struct video1394_wait v; - struct dma_iso_ctx *d; - int i = 0; - - if (unlikely(copy_from_user(&v, argp, sizeof(v)))) - return -EFAULT; - - d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel); - if (unlikely(d == NULL)) - return -EFAULT; - - if (unlikely(v.buffer > d->num_desc - 1)) { - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d out of range",v.buffer); - return -EINVAL; - } - - /* - * I change the way it works so that it returns - * the last received frame. - */ - spin_lock_irqsave(&d->lock, flags); - switch(d->buffer_status[v.buffer]) { - case VIDEO1394_BUFFER_READY: - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; - break; - case VIDEO1394_BUFFER_QUEUED: - if (cmd == VIDEO1394_IOC_LISTEN_POLL_BUFFER) { - /* for polling, return error code EINTR */ - spin_unlock_irqrestore(&d->lock, flags); - return -EINTR; - } - - spin_unlock_irqrestore(&d->lock, flags); - wait_event_interruptible(d->waitq, - video1394_buffer_state(d, v.buffer) == - VIDEO1394_BUFFER_READY); - if (signal_pending(current)) - return -EINTR; - spin_lock_irqsave(&d->lock, flags); - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; - break; - default: - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d is not queued",v.buffer); - spin_unlock_irqrestore(&d->lock, flags); - return -ESRCH; - } - - /* set time of buffer */ - v.filltime = d->buffer_time[v.buffer]; - - /* - * Look ahead to see how many more buffers have been received - */ - i=0; - while (d->buffer_status[(v.buffer+1)%(d->num_desc - 1)]== - VIDEO1394_BUFFER_READY) { - v.buffer=(v.buffer+1)%(d->num_desc - 1); - i++; - } - spin_unlock_irqrestore(&d->lock, flags); - - v.buffer=i; - if (unlikely(copy_to_user(argp, &v, sizeof(v)))) - return -EFAULT; - - return 0; - } - case VIDEO1394_IOC_TALK_QUEUE_BUFFER: - { - struct video1394_wait v; - unsigned int *psizes = NULL; - struct dma_iso_ctx *d; - int next_prg; - - if (copy_from_user(&v, argp, sizeof(v))) - return -EFAULT; - - d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel); - if (d == NULL) return -EFAULT; - - if (v.buffer >= d->num_desc - 1) { - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d out of range",v.buffer); - return -EINVAL; - } - - if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) { - int buf_size = d->nb_cmd * sizeof(*psizes); - struct video1394_queue_variable __user *p = argp; - unsigned int __user *qv; - - if (get_user(qv, &p->packet_sizes)) - return -EFAULT; - - psizes = memdup_user(qv, buf_size); - if (IS_ERR(psizes)) - return PTR_ERR(psizes); - } - - spin_lock_irqsave(&d->lock,flags); - - /* last_buffer is last_prg */ - next_prg = (d->last_buffer + 1) % d->num_desc; - if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) { - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d is already used",v.buffer); - spin_unlock_irqrestore(&d->lock,flags); - kfree(psizes); - return -EBUSY; - } - - if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) { - initialize_dma_it_prg_var_packet_queue( - d, next_prg, psizes, ohci); - } - - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; - - if (d->last_buffer >= 0) { - d->it_prg[d->last_buffer] - [ d->last_used_cmd[d->last_buffer] ].end.branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], - 0) & 0xfffffff0) | 0x3); - - d->it_prg[d->last_buffer] - [ d->last_used_cmd[d->last_buffer] ].begin.branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], - 0) & 0xfffffff0) | 0x3); - d->next_buffer[d->last_buffer] = (v.buffer + 1) % (d->num_desc - 1); - } - d->last_buffer = next_prg; - reprogram_dma_it_prg(d, d->last_buffer, v.buffer); - d->next_buffer[d->last_buffer] = -1; - - d->it_prg[d->last_buffer][d->last_used_cmd[d->last_buffer]].end.branchAddress = 0; - - spin_unlock_irqrestore(&d->lock,flags); - - if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) - { - DBGMSG(ohci->host->id, "Starting iso transmit DMA ctx=%d", - d->ctx); - put_timestamp(ohci, d, d->last_buffer); - dma_region_sync_for_device(&d->dma, - v.buffer * d->buf_size, d->buf_size); - - /* Tell the controller where the first program is */ - reg_write(ohci, d->cmdPtr, - dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0) | 0x3); - - /* Run IT context */ - reg_write(ohci, d->ctrlSet, 0x8000); - } - else { - /* Wake up dma context if necessary */ - if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { - DBGMSG(ohci->host->id, - "Waking up iso transmit dma ctx=%d", - d->ctx); - put_timestamp(ohci, d, d->last_buffer); - dma_region_sync_for_device(&d->dma, - v.buffer * d->buf_size, d->buf_size); - - reg_write(ohci, d->ctrlSet, 0x1000); - } - } - - kfree(psizes); - return 0; - - } - case VIDEO1394_IOC_TALK_WAIT_BUFFER: - { - struct video1394_wait v; - struct dma_iso_ctx *d; - - if (copy_from_user(&v, argp, sizeof(v))) - return -EFAULT; - - d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel); - if (d == NULL) return -EFAULT; - - if (v.buffer >= d->num_desc - 1) { - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d out of range",v.buffer); - return -EINVAL; - } - - switch(d->buffer_status[v.buffer]) { - case VIDEO1394_BUFFER_READY: - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; - return 0; - case VIDEO1394_BUFFER_QUEUED: - wait_event_interruptible(d->waitq, - (d->buffer_status[v.buffer] == VIDEO1394_BUFFER_READY)); - if (signal_pending(current)) - return -EINTR; - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; - return 0; - default: - PRINT(KERN_ERR, ohci->host->id, - "Buffer %d is not queued",v.buffer); - return -ESRCH; - } - } - default: - return -ENOTTY; - } -} - -/* - * This maps the vmalloced and reserved buffer to user space. - * - * FIXME: - * - PAGE_READONLY should suffice!? - * - remap_pfn_range is kind of inefficient for page by page remapping. - * But e.g. pte_alloc() does not work in modules ... :-( - */ - -static int video1394_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct file_ctx *ctx = file->private_data; - - if (ctx->current_ctx == NULL) { - PRINT(KERN_ERR, ctx->ohci->host->id, - "Current iso context not set"); - return -EINVAL; - } - - return dma_region_mmap(&ctx->current_ctx->dma, file, vma); -} - -static unsigned int video1394_poll(struct file *file, poll_table *pt) -{ - struct file_ctx *ctx; - unsigned int mask = 0; - unsigned long flags; - struct dma_iso_ctx *d; - int i; - - ctx = file->private_data; - d = ctx->current_ctx; - if (d == NULL) { - PRINT(KERN_ERR, ctx->ohci->host->id, - "Current iso context not set"); - return POLLERR; - } - - poll_wait(file, &d->waitq, pt); - - spin_lock_irqsave(&d->lock, flags); - for (i = 0; i < d->num_desc; i++) { - if (d->buffer_status[i] == VIDEO1394_BUFFER_READY) { - mask |= POLLIN | POLLRDNORM; - break; - } - } - spin_unlock_irqrestore(&d->lock, flags); - - return mask; -} - -static int video1394_open(struct inode *inode, struct file *file) -{ - int i = ieee1394_file_to_instance(file); - struct ti_ohci *ohci; - struct file_ctx *ctx; - - ohci = hpsb_get_hostinfo_bykey(&video1394_highlevel, i); - if (ohci == NULL) - return -EIO; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) { - PRINT(KERN_ERR, ohci->host->id, "Cannot malloc file_ctx"); - return -ENOMEM; - } - - ctx->ohci = ohci; - INIT_LIST_HEAD(&ctx->context_list); - ctx->current_ctx = NULL; - file->private_data = ctx; - - return nonseekable_open(inode, file); -} - -static int video1394_release(struct inode *inode, struct file *file) -{ - struct file_ctx *ctx = file->private_data; - struct ti_ohci *ohci = ctx->ohci; - struct list_head *lh, *next; - u64 mask; - - list_for_each_safe(lh, next, &ctx->context_list) { - struct dma_iso_ctx *d; - d = list_entry(lh, struct dma_iso_ctx, link); - mask = (u64) 1 << d->channel; - - if (!(ohci->ISO_channel_usage & mask)) - PRINT(KERN_ERR, ohci->host->id, "On release: Channel %d " - "is not being used", d->channel); - else - ohci->ISO_channel_usage &= ~mask; - DBGMSG(ohci->host->id, "On release: Iso %s context " - "%d stop listening on channel %d", - d->type == OHCI_ISO_RECEIVE ? "receive" : "transmit", - d->ctx, d->channel); - free_dma_iso_ctx(d); - } - - kfree(ctx); - file->private_data = NULL; - - return 0; -} - -#ifdef CONFIG_COMPAT -static long video1394_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg); -#endif - -static struct cdev video1394_cdev; -static const struct file_operations video1394_fops= -{ - .owner = THIS_MODULE, - .unlocked_ioctl = video1394_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = video1394_compat_ioctl, -#endif - .poll = video1394_poll, - .mmap = video1394_mmap, - .open = video1394_open, - .release = video1394_release, - .llseek = no_llseek, -}; - -/*** HOTPLUG STUFF **********************************************************/ -/* - * Export information about protocols/devices supported by this driver. - */ -#ifdef MODULE -static const struct ieee1394_device_id video1394_id_table[] = { - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = CAMERA_SW_VERSION_ENTRY & 0xffffff - }, - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = (CAMERA_SW_VERSION_ENTRY + 1) & 0xffffff - }, - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, - .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = (CAMERA_SW_VERSION_ENTRY + 2) & 0xffffff - }, - { } -}; - -MODULE_DEVICE_TABLE(ieee1394, video1394_id_table); -#endif /* MODULE */ - -static struct hpsb_protocol_driver video1394_driver = { - .name = VIDEO1394_DRIVER_NAME, -}; - - -static void video1394_add_host (struct hpsb_host *host) -{ - struct ti_ohci *ohci; - int minor; - - /* We only work with the OHCI-1394 driver */ - if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) - return; - - ohci = (struct ti_ohci *)host->hostdata; - - if (!hpsb_create_hostinfo(&video1394_highlevel, host, 0)) { - PRINT(KERN_ERR, ohci->host->id, "Cannot allocate hostinfo"); - return; - } - - hpsb_set_hostinfo(&video1394_highlevel, host, ohci); - hpsb_set_hostinfo_key(&video1394_highlevel, host, ohci->host->id); - - minor = IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id; - device_create(hpsb_protocol_class, NULL, MKDEV(IEEE1394_MAJOR, minor), - NULL, "%s-%d", VIDEO1394_DRIVER_NAME, ohci->host->id); -} - - -static void video1394_remove_host (struct hpsb_host *host) -{ - struct ti_ohci *ohci = hpsb_get_hostinfo(&video1394_highlevel, host); - - if (ohci) - device_destroy(hpsb_protocol_class, MKDEV(IEEE1394_MAJOR, - IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id)); - return; -} - - -static struct hpsb_highlevel video1394_highlevel = { - .name = VIDEO1394_DRIVER_NAME, - .add_host = video1394_add_host, - .remove_host = video1394_remove_host, -}; - -MODULE_AUTHOR("Sebastien Rougeaux "); -MODULE_DESCRIPTION("driver for digital video on OHCI board"); -MODULE_SUPPORTED_DEVICE(VIDEO1394_DRIVER_NAME); -MODULE_LICENSE("GPL"); - -#ifdef CONFIG_COMPAT - -#define VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER \ - _IOW ('#', 0x12, struct video1394_wait32) -#define VIDEO1394_IOC32_LISTEN_WAIT_BUFFER \ - _IOWR('#', 0x13, struct video1394_wait32) -#define VIDEO1394_IOC32_TALK_WAIT_BUFFER \ - _IOW ('#', 0x17, struct video1394_wait32) -#define VIDEO1394_IOC32_LISTEN_POLL_BUFFER \ - _IOWR('#', 0x18, struct video1394_wait32) - -struct video1394_wait32 { - u32 channel; - u32 buffer; - struct compat_timeval filltime; -}; - -static int video1394_wr_wait32(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct video1394_wait32 __user *argp = (void __user *)arg; - struct video1394_wait32 wait32; - struct video1394_wait wait; - mm_segment_t old_fs; - int ret; - - if (copy_from_user(&wait32, argp, sizeof(wait32))) - return -EFAULT; - - wait.channel = wait32.channel; - wait.buffer = wait32.buffer; - wait.filltime.tv_sec = (time_t)wait32.filltime.tv_sec; - wait.filltime.tv_usec = (suseconds_t)wait32.filltime.tv_usec; - - old_fs = get_fs(); - set_fs(KERNEL_DS); - if (cmd == VIDEO1394_IOC32_LISTEN_WAIT_BUFFER) - ret = video1394_ioctl(file, - VIDEO1394_IOC_LISTEN_WAIT_BUFFER, - (unsigned long) &wait); - else - ret = video1394_ioctl(file, - VIDEO1394_IOC_LISTEN_POLL_BUFFER, - (unsigned long) &wait); - set_fs(old_fs); - - if (!ret) { - wait32.channel = wait.channel; - wait32.buffer = wait.buffer; - wait32.filltime.tv_sec = (int)wait.filltime.tv_sec; - wait32.filltime.tv_usec = (int)wait.filltime.tv_usec; - - if (copy_to_user(argp, &wait32, sizeof(wait32))) - ret = -EFAULT; - } - - return ret; -} - -static int video1394_w_wait32(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct video1394_wait32 wait32; - struct video1394_wait wait; - mm_segment_t old_fs; - int ret; - - if (copy_from_user(&wait32, (void __user *)arg, sizeof(wait32))) - return -EFAULT; - - wait.channel = wait32.channel; - wait.buffer = wait32.buffer; - wait.filltime.tv_sec = (time_t)wait32.filltime.tv_sec; - wait.filltime.tv_usec = (suseconds_t)wait32.filltime.tv_usec; - - old_fs = get_fs(); - set_fs(KERNEL_DS); - if (cmd == VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER) - ret = video1394_ioctl(file, - VIDEO1394_IOC_LISTEN_QUEUE_BUFFER, - (unsigned long) &wait); - else - ret = video1394_ioctl(file, - VIDEO1394_IOC_TALK_WAIT_BUFFER, - (unsigned long) &wait); - set_fs(old_fs); - - return ret; -} - -static int video1394_queue_buf32(struct file *file, unsigned int cmd, unsigned long arg) -{ - return -EFAULT; /* ??? was there before. */ - - return video1394_ioctl(file, - VIDEO1394_IOC_TALK_QUEUE_BUFFER, arg); -} - -static long video1394_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg) -{ - switch (cmd) { - case VIDEO1394_IOC_LISTEN_CHANNEL: - case VIDEO1394_IOC_UNLISTEN_CHANNEL: - case VIDEO1394_IOC_TALK_CHANNEL: - case VIDEO1394_IOC_UNTALK_CHANNEL: - return video1394_ioctl(f, cmd, arg); - - case VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER: - return video1394_w_wait32(f, cmd, arg); - case VIDEO1394_IOC32_LISTEN_WAIT_BUFFER: - return video1394_wr_wait32(f, cmd, arg); - case VIDEO1394_IOC_TALK_QUEUE_BUFFER: - return video1394_queue_buf32(f, cmd, arg); - case VIDEO1394_IOC32_TALK_WAIT_BUFFER: - return video1394_w_wait32(f, cmd, arg); - case VIDEO1394_IOC32_LISTEN_POLL_BUFFER: - return video1394_wr_wait32(f, cmd, arg); - default: - return -ENOIOCTLCMD; - } -} - -#endif /* CONFIG_COMPAT */ - -static void __exit video1394_exit_module (void) -{ - hpsb_unregister_protocol(&video1394_driver); - hpsb_unregister_highlevel(&video1394_highlevel); - cdev_del(&video1394_cdev); - PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module"); -} - -static int __init video1394_init_module (void) -{ - int ret; - - hpsb_init_highlevel(&video1394_highlevel); - - cdev_init(&video1394_cdev, &video1394_fops); - video1394_cdev.owner = THIS_MODULE; - ret = cdev_add(&video1394_cdev, IEEE1394_VIDEO1394_DEV, 16); - if (ret) { - PRINT_G(KERN_ERR, "video1394: unable to get minor device block"); - return ret; - } - - hpsb_register_highlevel(&video1394_highlevel); - - ret = hpsb_register_protocol(&video1394_driver); - if (ret) { - PRINT_G(KERN_ERR, "video1394: failed to register protocol"); - hpsb_unregister_highlevel(&video1394_highlevel); - cdev_del(&video1394_cdev); - return ret; - } - - PRINT_G(KERN_INFO, "Installed " VIDEO1394_DRIVER_NAME " module"); - return 0; -} - - -module_init(video1394_init_module); -module_exit(video1394_exit_module); diff --git a/drivers/ieee1394/video1394.h b/drivers/ieee1394/video1394.h deleted file mode 100644 index 9a89d9cc3c85..000000000000 --- a/drivers/ieee1394/video1394.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * video1394.h - driver for OHCI 1394 boards - * Copyright (C)1999,2000 Sebastien Rougeaux - * Peter Schlaile - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _VIDEO_1394_H -#define _VIDEO_1394_H - -#include "ieee1394-ioctl.h" - -#define VIDEO1394_DRIVER_NAME "video1394" - -#define VIDEO1394_MAX_SIZE 0x4000000 - -enum { - VIDEO1394_BUFFER_FREE = 0, - VIDEO1394_BUFFER_QUEUED, - VIDEO1394_BUFFER_READY -}; - -#define VIDEO1394_SYNC_FRAMES 0x00000001 -#define VIDEO1394_INCLUDE_ISO_HEADERS 0x00000002 -#define VIDEO1394_VARIABLE_PACKET_SIZE 0x00000004 - -struct video1394_mmap { - int channel; /* -1 to find an open channel in LISTEN/TALK */ - unsigned int sync_tag; - unsigned int nb_buffers; - unsigned int buf_size; - unsigned int packet_size; /* For VARIABLE_PACKET_SIZE: - Maximum packet size */ - unsigned int fps; - unsigned int syt_offset; - unsigned int flags; -}; - -/* For TALK_QUEUE_BUFFER with VIDEO1394_VARIABLE_PACKET_SIZE use */ -struct video1394_queue_variable { - unsigned int channel; - unsigned int buffer; - unsigned int __user * packet_sizes; /* Buffer of size: - buf_size / packet_size */ -}; - -struct video1394_wait { - unsigned int channel; - unsigned int buffer; - struct timeval filltime; /* time of buffer full */ -}; - - -#endif -- cgit v1.2.3-70-g09d2 From 6370a6ad3b53df90b4700977f7718118a2cd524a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 11 Oct 2010 15:12:27 +0200 Subject: workqueue: add and use WQ_MEM_RECLAIM flag Add WQ_MEM_RECLAIM flag which currently maps to WQ_RESCUER, mark WQ_RESCUER as internal and replace all external WQ_RESCUER usages to WQ_MEM_RECLAIM. This makes the API users express the intent of the workqueue instead of indicating the internal mechanism used to guarantee forward progress. This is also to make it cleaner to add more semantics to WQ_MEM_RECLAIM. For example, if deemed necessary, memory reclaim workqueues can be made highpri. This patch doesn't introduce any functional change. Signed-off-by: Tejun Heo Cc: Jeff Garzik Cc: Dave Chinner Cc: Steven Whitehouse --- Documentation/workqueue.txt | 29 +++++++++++++++-------------- drivers/ata/libata-sff.c | 2 +- fs/gfs2/main.c | 2 +- fs/xfs/linux-2.6/xfs_buf.c | 2 +- include/linux/workqueue.h | 11 ++++++----- kernel/workqueue.c | 7 +++++++ 6 files changed, 31 insertions(+), 22 deletions(-) (limited to 'Documentation') diff --git a/Documentation/workqueue.txt b/Documentation/workqueue.txt index e4498a2872c3..996a27d9b8db 100644 --- a/Documentation/workqueue.txt +++ b/Documentation/workqueue.txt @@ -196,11 +196,11 @@ resources, scheduled and executed. suspend operations. Work items on the wq are drained and no new work item starts execution until thawed. - WQ_RESCUER + WQ_MEM_RECLAIM All wq which might be used in the memory reclaim paths _MUST_ - have this flag set. This reserves one worker exclusively for - the execution of this wq under memory pressure. + have this flag set. The wq is guaranteed to have at least one + execution context regardless of memory pressure. WQ_HIGHPRI @@ -356,11 +356,11 @@ If q1 has WQ_CPU_INTENSIVE set, 6. Guidelines -* Do not forget to use WQ_RESCUER if a wq may process work items which - are used during memory reclaim. Each wq with WQ_RESCUER set has one - rescuer thread reserved for it. If there is dependency among - multiple work items used during memory reclaim, they should be - queued to separate wq each with WQ_RESCUER. +* Do not forget to use WQ_MEM_RECLAIM if a wq may process work items + which are used during memory reclaim. Each wq with WQ_MEM_RECLAIM + set has an execution context reserved for it. If there is + dependency among multiple work items used during memory reclaim, + they should be queued to separate wq each with WQ_MEM_RECLAIM. * Unless strict ordering is required, there is no need to use ST wq. @@ -368,12 +368,13 @@ If q1 has WQ_CPU_INTENSIVE set, recommended. In most use cases, concurrency level usually stays well under the default limit. -* A wq serves as a domain for forward progress guarantee (WQ_RESCUER), - flush and work item attributes. Work items which are not involved - in memory reclaim and don't need to be flushed as a part of a group - of work items, and don't require any special attribute, can use one - of the system wq. There is no difference in execution - characteristics between using a dedicated wq and a system wq. +* A wq serves as a domain for forward progress guarantee + (WQ_MEM_RECLAIM, flush and work item attributes. Work items which + are not involved in memory reclaim and don't need to be flushed as a + part of a group of work items, and don't require any special + attribute, can use one of the system wq. There is no difference in + execution characteristics between using a dedicated wq and a system + wq. * Unless work items are expected to consume a huge amount of CPU cycles, using a bound wq is usually beneficial due to the increased diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index e30c537cce32..f5296bb19ec0 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -3335,7 +3335,7 @@ void ata_sff_port_init(struct ata_port *ap) int __init ata_sff_init(void) { - ata_sff_wq = alloc_workqueue("ata_sff", WQ_RESCUER, WQ_MAX_ACTIVE); + ata_sff_wq = alloc_workqueue("ata_sff", WQ_MEM_RECLAIM, WQ_MAX_ACTIVE); if (!ata_sff_wq) return -ENOMEM; diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index b1e9630eb46a..1c5f46075d52 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -140,7 +140,7 @@ static int __init init_gfs2_fs(void) error = -ENOMEM; gfs_recovery_wq = alloc_workqueue("gfs_recovery", - WQ_NON_REENTRANT | WQ_RESCUER, 0); + WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0); if (!gfs_recovery_wq) goto fail_wq; diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 286e36e21dae..6838aefca71f 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -1933,7 +1933,7 @@ xfs_buf_init(void) goto out; xfslogd_workqueue = alloc_workqueue("xfslogd", - WQ_RESCUER | WQ_HIGHPRI, 1); + WQ_MEM_RECLAIM | WQ_HIGHPRI, 1); if (!xfslogd_workqueue) goto out_free_buf_zone; diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index e33ff4a91703..03bbe903e5ce 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -243,11 +243,12 @@ enum { WQ_NON_REENTRANT = 1 << 0, /* guarantee non-reentrance */ WQ_UNBOUND = 1 << 1, /* not bound to any cpu */ WQ_FREEZEABLE = 1 << 2, /* freeze during suspend */ - WQ_RESCUER = 1 << 3, /* has an rescue worker */ + WQ_MEM_RECLAIM = 1 << 3, /* may be used for memory reclaim */ WQ_HIGHPRI = 1 << 4, /* high priority */ WQ_CPU_INTENSIVE = 1 << 5, /* cpu instensive workqueue */ WQ_DYING = 1 << 6, /* internal: workqueue is dying */ + WQ_RESCUER = 1 << 7, /* internal: workqueue has rescuer */ WQ_MAX_ACTIVE = 512, /* I like 512, better ideas? */ WQ_MAX_UNBOUND_PER_CPU = 4, /* 4 * #cpus for unbound wq */ @@ -309,7 +310,7 @@ __alloc_workqueue_key(const char *name, unsigned int flags, int max_active, /** * alloc_ordered_workqueue - allocate an ordered workqueue * @name: name of the workqueue - * @flags: WQ_* flags (only WQ_FREEZEABLE and WQ_RESCUER are meaningful) + * @flags: WQ_* flags (only WQ_FREEZEABLE and WQ_MEM_RECLAIM are meaningful) * * Allocate an ordered workqueue. An ordered workqueue executes at * most one work item at any given time in the queued order. They are @@ -325,11 +326,11 @@ alloc_ordered_workqueue(const char *name, unsigned int flags) } #define create_workqueue(name) \ - alloc_workqueue((name), WQ_RESCUER, 1) + alloc_workqueue((name), WQ_MEM_RECLAIM, 1) #define create_freezeable_workqueue(name) \ - alloc_workqueue((name), WQ_FREEZEABLE | WQ_UNBOUND | WQ_RESCUER, 1) + alloc_workqueue((name), WQ_FREEZEABLE | WQ_UNBOUND | WQ_MEM_RECLAIM, 1) #define create_singlethread_workqueue(name) \ - alloc_workqueue((name), WQ_UNBOUND | WQ_RESCUER, 1) + alloc_workqueue((name), WQ_UNBOUND | WQ_MEM_RECLAIM, 1) extern void destroy_workqueue(struct workqueue_struct *wq); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index b57a8babdec3..2c6871cbcbee 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -2847,6 +2847,13 @@ struct workqueue_struct *__alloc_workqueue_key(const char *name, struct workqueue_struct *wq; unsigned int cpu; + /* + * Workqueues which may be used during memory reclaim should + * have a rescuer to guarantee forward progress. + */ + if (flags & WQ_MEM_RECLAIM) + flags |= WQ_RESCUER; + /* * Unbound workqueues aren't concurrency managed and should be * dispatched to workers immediately. -- cgit v1.2.3-70-g09d2 From 7bdb0d18bfd381cc5491eb95973ec5604b356c7e Mon Sep 17 00:00:00 2001 From: Tristan Ye Date: Mon, 11 Oct 2010 16:46:39 +0800 Subject: ocfs2: Add a mount option "coherency=*" to handle cluster coherency for O_DIRECT writes. Currently, the default behavior of O_DIRECT writes was allowing concurrent writing among nodes to the same file, with no cluster coherency guaranteed (no EX lock held). This can leave stale data in the cache for buffered reads on other nodes. The new mount option introduce a chance to choose two different behaviors for O_DIRECT writes: * coherency=full, as the default value, will disallow concurrent O_DIRECT writes by taking EX locks. * coherency=buffered, allow concurrent O_DIRECT writes without EX lock among nodes, which gains high performance at risk of getting stale data on other nodes. Signed-off-by: Tristan Ye Signed-off-by: Joel Becker --- Documentation/filesystems/ocfs2.txt | 7 +++++++ fs/ocfs2/file.c | 29 +++++++++++++++++++++++++++-- fs/ocfs2/ocfs2.h | 3 +++ fs/ocfs2/super.c | 15 +++++++++++++++ 4 files changed, 52 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/ocfs2.txt b/Documentation/filesystems/ocfs2.txt index 1f7ae144f6d8..5393e6611691 100644 --- a/Documentation/filesystems/ocfs2.txt +++ b/Documentation/filesystems/ocfs2.txt @@ -87,3 +87,10 @@ dir_resv_level= (*) By default, directory reservations will scale with file reservations - users should rarely need to change this value. If allocation reservations are turned off, this option will have no effect. +coherency=full (*) Disallow concurrent O_DIRECT writes, cluster inode + lock will be taken to force other nodes drop cache, + therefore full cluster coherency is guaranteed even + for O_DIRECT writes. +coherency=buffered Allow concurrent O_DIRECT writes without EX lock among + nodes, which gains high performance at risk of getting + stale data on other nodes. diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 13af9937bdda..9e8cc4346b76 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2225,6 +2225,8 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb, struct file *file = iocb->ki_filp; struct inode *inode = file->f_path.dentry->d_inode; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + int full_coherency = !(osb->s_mount_opt & + OCFS2_MOUNT_COHERENCY_BUFFERED); mlog_entry("(0x%p, %u, '%.*s')\n", file, (unsigned int)nr_segs, @@ -2248,14 +2250,37 @@ relock: have_alloc_sem = 1; } - /* concurrent O_DIRECT writes are allowed */ - rw_level = !direct_io; + /* + * Concurrent O_DIRECT writes are allowed with + * mount_option "coherency=buffered". + */ + rw_level = (!direct_io || full_coherency); + ret = ocfs2_rw_lock(inode, rw_level); if (ret < 0) { mlog_errno(ret); goto out_sems; } + /* + * O_DIRECT writes with "coherency=full" need to take EX cluster + * inode_lock to guarantee coherency. + */ + if (direct_io && full_coherency) { + /* + * We need to take and drop the inode lock to force + * other nodes to drop their caches. Buffered I/O + * already does this in write_begin(). + */ + ret = ocfs2_inode_lock(inode, NULL, 1); + if (ret < 0) { + mlog_errno(ret); + goto out_sems; + } + + ocfs2_inode_unlock(inode, 1); + } + can_do_direct = direct_io; ret = ocfs2_prepare_inode_for_write(file, ppos, iocb->ki_left, appending, diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index 687e291d73f2..3064feef1430 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -263,6 +263,9 @@ enum ocfs2_mount_options control lists */ OCFS2_MOUNT_USRQUOTA = 1 << 10, /* We support user quotas */ OCFS2_MOUNT_GRPQUOTA = 1 << 11, /* We support group quotas */ + + OCFS2_MOUNT_COHERENCY_BUFFERED = 1 << 12 /* Allow concurrent O_DIRECT + writes */ }; #define OCFS2_OSB_SOFT_RO 0x0001 diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index b578644b6637..9122d59f8127 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -177,6 +177,8 @@ enum { Opt_noacl, Opt_usrquota, Opt_grpquota, + Opt_coherency_buffered, + Opt_coherency_full, Opt_resv_level, Opt_dir_resv_level, Opt_err, @@ -205,6 +207,8 @@ static const match_table_t tokens = { {Opt_noacl, "noacl"}, {Opt_usrquota, "usrquota"}, {Opt_grpquota, "grpquota"}, + {Opt_coherency_buffered, "coherency=buffered"}, + {Opt_coherency_full, "coherency=full"}, {Opt_resv_level, "resv_level=%u"}, {Opt_dir_resv_level, "dir_resv_level=%u"}, {Opt_err, NULL} @@ -1452,6 +1456,12 @@ static int ocfs2_parse_options(struct super_block *sb, case Opt_grpquota: mopt->mount_opt |= OCFS2_MOUNT_GRPQUOTA; break; + case Opt_coherency_buffered: + mopt->mount_opt |= OCFS2_MOUNT_COHERENCY_BUFFERED; + break; + case Opt_coherency_full: + mopt->mount_opt &= ~OCFS2_MOUNT_COHERENCY_BUFFERED; + break; case Opt_acl: mopt->mount_opt |= OCFS2_MOUNT_POSIX_ACL; mopt->mount_opt &= ~OCFS2_MOUNT_NO_POSIX_ACL; @@ -1550,6 +1560,11 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt) if (opts & OCFS2_MOUNT_GRPQUOTA) seq_printf(s, ",grpquota"); + if (opts & OCFS2_MOUNT_COHERENCY_BUFFERED) + seq_printf(s, ",coherency=buffered"); + else + seq_printf(s, ",coherency=full"); + if (opts & OCFS2_MOUNT_NOUSERXATTR) seq_printf(s, ",nouser_xattr"); else -- cgit v1.2.3-70-g09d2 From 25ade601a0f97453c6f511ebfae9339e06a28d75 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 30 Sep 2010 20:33:11 +0200 Subject: genirq-update-kerneldoc.patch Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar --- Documentation/DocBook/genericirq.tmpl | 84 ++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 32 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/genericirq.tmpl b/Documentation/DocBook/genericirq.tmpl index 1448b33fd222..fb10fd08c05c 100644 --- a/Documentation/DocBook/genericirq.tmpl +++ b/Documentation/DocBook/genericirq.tmpl @@ -28,7 +28,7 @@ - 2005-2006 + 2005-2010 Thomas Gleixner @@ -100,6 +100,10 @@ Edge type Simple type + During the implementation we identified another type: + + Fast EOI type + In the SMP world of the __do_IRQ() super-handler another type was identified: @@ -153,6 +157,7 @@ is still available. This leads to a kind of duality for the time being. Over time the new model should be used in more and more architectures, as it enables smaller and cleaner IRQ subsystems. + It's deprecated for three years now and about to be removed. @@ -217,6 +222,7 @@ handle_level_irq handle_edge_irq + handle_fasteoi_irq handle_simple_irq handle_percpu_irq @@ -233,33 +239,33 @@ are used by the default flow implementations. The following helper functions are implemented (simplified excerpt): -default_enable(irq) +default_enable(struct irq_data *data) { - desc->chip->unmask(irq); + desc->chip->irq_unmask(data); } -default_disable(irq) +default_disable(struct irq_data *data) { - if (!delay_disable(irq)) - desc->chip->mask(irq); + if (!delay_disable(data)) + desc->chip->irq_mask(data); } -default_ack(irq) +default_ack(struct irq_data *data) { - chip->ack(irq); + chip->irq_ack(data); } -default_mask_ack(irq) +default_mask_ack(struct irq_data *data) { - if (chip->mask_ack) { - chip->mask_ack(irq); + if (chip->irq_mask_ack) { + chip->irq_mask_ack(data); } else { - chip->mask(irq); - chip->ack(irq); + chip->irq_mask(data); + chip->irq_ack(data); } } -noop(irq) +noop(struct irq_data *data)) { } @@ -278,12 +284,27 @@ noop(irq) The following control flow is implemented (simplified excerpt): -desc->chip->start(); +desc->chip->irq_mask(); handle_IRQ_event(desc->action); -desc->chip->end(); +desc->chip->irq_unmask(); - + + + Default Fast EOI IRQ flow handler + + handle_fasteoi_irq provides a generic implementation + for interrupts, which only need an EOI at the end of + the handler + + + The following control flow is implemented (simplified excerpt): + +handle_IRQ_event(desc->action); +desc->chip->irq_eoi(); + + + Default Edge IRQ flow handler @@ -294,20 +315,19 @@ desc->chip->end(); The following control flow is implemented (simplified excerpt): if (desc->status & running) { - desc->chip->hold(); + desc->chip->irq_mask(); desc->status |= pending | masked; return; } -desc->chip->start(); +desc->chip->irq_ack(); desc->status |= running; do { if (desc->status & masked) - desc->chip->enable(); + desc->chip->irq_unmask(); desc->status &= ~pending; handle_IRQ_event(desc->action); } while (status & pending); desc->status &= ~running; -desc->chip->end(); @@ -342,9 +362,9 @@ handle_IRQ_event(desc->action); The following control flow is implemented (simplified excerpt): -desc->chip->start(); handle_IRQ_event(desc->action); -desc->chip->end(); +if (desc->chip->irq_eoi) + desc->chip->irq_eoi(); @@ -375,8 +395,7 @@ desc->chip->end(); mechanism. (It's necessary to enable CONFIG_HARDIRQS_SW_RESEND when you want to use the delayed interrupt disable feature and your hardware is not capable of retriggering an interrupt.) - The delayed interrupt disable can be runtime enabled, per interrupt, - by setting the IRQ_DELAYED_DISABLE flag in the irq_desc status field. + The delayed interrupt disable is not configurable. @@ -387,13 +406,13 @@ desc->chip->end(); contains all the direct chip relevant functions, which can be utilized by the irq flow implementations. - ack() - mask_ack() - Optional, recommended for performance - mask() - unmask() - retrigger() - Optional - set_type() - Optional - set_wake() - Optional + irq_ack() + irq_mask_ack() - Optional, recommended for performance + irq_mask() + irq_unmask() + irq_retrigger() - Optional + irq_set_type() - Optional + irq_set_wake() - Optional These primitives are strictly intended to mean what they say: ack means ACK, masking means masking of an IRQ line, etc. It is up to the flow @@ -458,6 +477,7 @@ desc->chip->end(); This chapter contains the autogenerated documentation of the internal functions. +!Ikernel/irq/irqdesc.c !Ikernel/irq/handle.c !Ikernel/irq/chip.c -- cgit v1.2.3-70-g09d2 From f3016fa591c788d6d545ef7907e24c8b5d788759 Mon Sep 17 00:00:00 2001 From: Mingkai Hu Date: Tue, 12 Oct 2010 18:18:33 +0800 Subject: powerpc/of: add eSPI controller dts bindings and DTS modification Also modifiy the document of cell-index in SPI controller. Add the SPI flash(s25fl128p01) support on p4080ds and mpc8536ds board. Signed-off-by: Mingkai Hu Signed-off-by: Grant Likely --- Documentation/powerpc/dts-bindings/fsl/spi.txt | 24 +++++++++++- arch/powerpc/boot/dts/mpc8536ds.dts | 52 ++++++++++++++++++++++++++ arch/powerpc/boot/dts/p4080ds.dts | 11 ++---- 3 files changed, 79 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/powerpc/dts-bindings/fsl/spi.txt b/Documentation/powerpc/dts-bindings/fsl/spi.txt index 80510c018eea..777abd7399d5 100644 --- a/Documentation/powerpc/dts-bindings/fsl/spi.txt +++ b/Documentation/powerpc/dts-bindings/fsl/spi.txt @@ -1,7 +1,9 @@ * SPI (Serial Peripheral Interface) Required properties: -- cell-index : SPI controller index. +- cell-index : QE SPI subblock index. + 0: QE subblock SPI1 + 1: QE subblock SPI2 - compatible : should be "fsl,spi". - mode : the SPI operation mode, it can be "cpu" or "cpu-qe". - reg : Offset and length of the register set for the device @@ -29,3 +31,23 @@ Example: gpios = <&gpio 18 1 // device reg=<0> &gpio 19 1>; // device reg=<1> }; + + +* eSPI (Enhanced Serial Peripheral Interface) + +Required properties: +- compatible : should be "fsl,mpc8536-espi". +- reg : Offset and length of the register set for the device. +- interrupts : should contain eSPI interrupt, the device has one interrupt. +- fsl,espi-num-chipselects : the number of the chipselect signals. + +Example: + spi@110000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,mpc8536-espi"; + reg = <0x110000 0x1000>; + interrupts = <53 0x2>; + interrupt-parent = <&mpic>; + fsl,espi-num-chipselects = <4>; + }; diff --git a/arch/powerpc/boot/dts/mpc8536ds.dts b/arch/powerpc/boot/dts/mpc8536ds.dts index 815cebb2e3e5..a75c10eed269 100644 --- a/arch/powerpc/boot/dts/mpc8536ds.dts +++ b/arch/powerpc/boot/dts/mpc8536ds.dts @@ -108,6 +108,58 @@ }; }; + spi@7000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,mpc8536-espi"; + reg = <0x7000 0x1000>; + interrupts = <59 0x2>; + interrupt-parent = <&mpic>; + fsl,espi-num-chipselects = <4>; + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,s25sl12801"; + reg = <0>; + spi-max-frequency = <40000000>; + partition@u-boot { + label = "u-boot"; + reg = <0x00000000 0x00100000>; + read-only; + }; + partition@kernel { + label = "kernel"; + reg = <0x00100000 0x00500000>; + read-only; + }; + partition@dtb { + label = "dtb"; + reg = <0x00600000 0x00100000>; + read-only; + }; + partition@fs { + label = "file system"; + reg = <0x00700000 0x00900000>; + }; + }; + flash@1 { + compatible = "spansion,s25sl12801"; + reg = <1>; + spi-max-frequency = <40000000>; + }; + flash@2 { + compatible = "spansion,s25sl12801"; + reg = <2>; + spi-max-frequency = <40000000>; + }; + flash@3 { + compatible = "spansion,s25sl12801"; + reg = <3>; + spi-max-frequency = <40000000>; + }; + }; + dma@21300 { #address-cells = <1>; #size-cells = <1>; diff --git a/arch/powerpc/boot/dts/p4080ds.dts b/arch/powerpc/boot/dts/p4080ds.dts index 2f0de24e3822..5b7fc29dd6cf 100644 --- a/arch/powerpc/boot/dts/p4080ds.dts +++ b/arch/powerpc/boot/dts/p4080ds.dts @@ -236,22 +236,19 @@ }; spi@110000 { - cell-index = <0>; #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,espi"; + compatible = "fsl,p4080-espi", "fsl,mpc8536-espi"; reg = <0x110000 0x1000>; interrupts = <53 0x2>; interrupt-parent = <&mpic>; - espi,num-ss-bits = <4>; - mode = "cpu"; + fsl,espi-num-chipselects = <4>; - fsl_m25p80@0 { + flash@0 { #address-cells = <1>; #size-cells = <1>; - compatible = "fsl,espi-flash"; + compatible = "spansion,s25sl12801"; reg = <0>; - linux,modalias = "fsl_m25p80"; spi-max-frequency = <40000000>; /* input clock */ partition@u-boot { label = "u-boot"; -- cgit v1.2.3-70-g09d2 From 32af08987b8f093195f8eef3ee4e1dbcfee891cd Mon Sep 17 00:00:00 2001 From: Nicolas Palix Date: Wed, 13 Oct 2010 00:49:07 +0200 Subject: Coccinelle: Update documentation - Add information about use of the C={1,2} make flag - Add a description of the new chain mode mechanism - Add a link to the wiki Signed-off-by: Nicolas Palix Signed-off-by: Julia Lawall Signed-off-by: Michal Marek --- Documentation/coccinelle.txt | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/coccinelle.txt b/Documentation/coccinelle.txt index cd2b02837066..de51a3eb17b3 100644 --- a/Documentation/coccinelle.txt +++ b/Documentation/coccinelle.txt @@ -24,6 +24,9 @@ of many distributions, e.g. : You can get the latest version released from the Coccinelle homepage at http://coccinelle.lip6.fr/ +Information and tips about Coccinelle are also provided on the wiki +pages at http://cocci.ekstranet.diku.dk/wiki/doku.php + Once you have it, run the following command: ./configure @@ -41,20 +44,22 @@ A Coccinelle-specific target is defined in the top level Makefile. This target is named 'coccicheck' and calls the 'coccicheck' front-end in the 'scripts' directory. -Four modes are defined: report, patch, context, and org. The mode to +Four modes are defined: patch, report, context, and org. The mode to use is specified by setting the MODE variable with 'MODE='. +'patch' proposes a fix, when possible. + 'report' generates a list in the following format: file:line:column-column: message -'patch' proposes a fix, when possible. - 'context' highlights lines of interest and their context in a diff-like style.Lines of interest are indicated with '-'. 'org' generates a report in the Org mode format of Emacs. -Note that not all semantic patches implement all modes. +Note that not all semantic patches implement all modes. For easy use +of Coccinelle, the default mode is "chain" which tries the previous +modes in the order above until one succeeds. To make a report for every semantic patch, run the following command: @@ -68,9 +73,9 @@ To produce patches, run: The coccicheck target applies every semantic patch available in the -subdirectories of 'scripts/coccinelle' to the entire Linux kernel. +sub-directories of 'scripts/coccinelle' to the entire Linux kernel. -For each semantic patch, a changelog message is proposed. It gives a +For each semantic patch, a commit message is proposed. It gives a description of the problem being checked by the semantic patch, and includes a reference to Coccinelle. @@ -93,12 +98,35 @@ or make coccicheck COCCI= MODE=report + Using Coccinelle on (modified) files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To apply Coccinelle on a file basis, instead of a directory basis, the +following command may be used: + + make C=1 CHECK="scripts/coccicheck" + +To check only newly edited code, use the value 2 for the C flag, i.e. + + make C=2 CHECK="scripts/coccicheck" + +This runs every semantic patch in scripts/coccinelle by default. The +COCCI variable may additionally be used to only apply a single +semantic patch as shown in the previous section. + +The "chain" mode is the default. You can select another one with the +MODE variable explained above. + +In this mode, there is no information about semantic patches +displayed, and no commit message proposed. + + Proposing new semantic patches ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ New semantic patches can be proposed and submitted by kernel developers. For sake of clarity, they should be organized in the -subdirectories of 'scripts/coccinelle/'. +sub-directories of 'scripts/coccinelle/'. Detailed description of the 'report' mode -- cgit v1.2.3-70-g09d2 From f20ce779bb31a90985b0daea2bf0aaf009d21b81 Mon Sep 17 00:00:00 2001 From: Kumar Sanghvi Date: Tue, 12 Oct 2010 20:17:25 +0000 Subject: Documentation: Update Phonet doc for Pipe controller changes Updates to Phonet doc for Pipe controller 'connect' socket implementation and changes related to socket options. Signed-off-by: Kumar Sanghvi Signed-off-by: David S. Miller --- Documentation/networking/phonet.txt | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/phonet.txt b/Documentation/networking/phonet.txt index 2d9bc2b711fc..24ad2adba6e5 100644 --- a/Documentation/networking/phonet.txt +++ b/Documentation/networking/phonet.txt @@ -199,33 +199,29 @@ between itself and a remote pipe-end point (e.g. modem). The implementation adds socket options at SOL_PNPIPE level: - PNPIPE_CREATE - It accepts an integer argument where-in - lower order 16 bits: pn_dev and pn_port pair for remote pep. - higher order 16 bits: 8 bit pipe-handle - - It sends a PNS_PEP_CONNECT_REQ on sequenced socket itself. On getting - PNS_PEP_CONNECT_RESP, it sends PNS_PEP_CONNECT_REQ to remote pep. On - getting response from remote pep, it selects the best possible Flow - control mechanism supported by remote-pep (modem) and then it sends - PNS_PEP_CREATED_IND to the sequenced socket and to the remote pep. - - It then updates the pipe state associated with the sequenced socket to - be PIPE_DISABLED. + PNPIPE_PIPE_HANDLE + It accepts an integer argument for setting value of pipe handle. PNPIPE_ENABLE accepts one integer value (int). If set to zero, the pipe is disabled. If the value is non-zero, the pipe is enabled. If the pipe is not (yet) connected, ENOTCONN is error is returned. - PNPIPE_DESTROY - This will send out PNS_PEP_DISCONNECT_REQ on the sequenced socket and - the remote pep. - It will also update the pipe state associated with the sequenced socket - to PIPE_IDLE +The implementation also adds socket 'connect'. On calling the 'connect', pipe +will be created between the source socket and the destination, and the pipe +state will be set to PIPE_DISABLED. After a pipe has been created and enabled successfully, the Pipe data can be exchanged between the host-pep and remote-pep (modem). +User-space would typically follow below sequence with Pipe controller:- +-socket +-bind +-setsockopt for PNPIPE_PIPE_HANDLE +-connect +-setsockopt for PNPIPE_ENCAP_IP +-setsockopt for PNPIPE_ENABLE + + Authors ------- -- cgit v1.2.3-70-g09d2 From 0f55a2f3d496133dd22f1e4e49fb7301da87c7bb Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 14 Oct 2010 12:10:18 +0900 Subject: kprobes: Update document about irq disabled state in kprobe handler Update kprobes.txt about interrupts disabled state inside kprobes handlers, because optimized probe/boosted kretprobe run without disabling interrrupts on x86. Signed-off-by: Masami Hiramatsu Cc: Rusty Russell Cc: Ananth N Mavinakayanahalli Cc: 2nddept-manager@sdl.hitachi.co.jp LKML-Reference: <20101014031018.4100.64883.stgit@ltc236.sdl.hitachi.co.jp> Signed-off-by: Ingo Molnar --- Documentation/kprobes.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kprobes.txt b/Documentation/kprobes.txt index 1762b81fcdf2..741fe66d6eca 100644 --- a/Documentation/kprobes.txt +++ b/Documentation/kprobes.txt @@ -542,9 +542,11 @@ Kprobes does not use mutexes or allocate memory except during registration and unregistration. Probe handlers are run with preemption disabled. Depending on the -architecture, handlers may also run with interrupts disabled. In any -case, your handler should not yield the CPU (e.g., by attempting to -acquire a semaphore). +architecture and optimization state, handlers may also run with +interrupts disabled (e.g., kretprobe handlers and optimized kprobe +handlers run without interrupt disabled on x86/x86-64). In any case, +your handler should not yield the CPU (e.g., by attempting to acquire +a semaphore). Since a return probe is implemented by replacing the return address with the trampoline's address, stack backtraces and calls -- cgit v1.2.3-70-g09d2 From f996fc9671d088bd5f52a70f18c64bfe3d0e418f Mon Sep 17 00:00:00 2001 From: Bojan Smojver Date: Thu, 9 Sep 2010 23:06:23 +0200 Subject: PM / Hibernate: Compress hibernation image with LZO Compress hibernation image with LZO in order to save on I/O and therefore time to hibernate/thaw. [rjw: Added hibernate=nocompress command line option instead of just nocompress which would be confusing, fixed a couple of compiler warnings, fixed kerneldoc comments, minor cleanups.] Signed-off-by: Bojan Smojver Signed-off-by: Rafael J. Wysocki --- Documentation/kernel-parameters.txt | 5 + Documentation/power/swsusp.txt | 3 +- kernel/power/Kconfig | 2 + kernel/power/hibernate.c | 13 ++ kernel/power/power.h | 1 + kernel/power/swap.c | 290 +++++++++++++++++++++++++++++++++++- 6 files changed, 306 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 8dd7248508a9..2c98b18864c5 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2165,6 +2165,11 @@ and is between 256 and 4096 characters. It is defined in the file in units (needed only for swap files). See Documentation/power/swsusp-and-swap-files.txt + hibernate= [HIBERNATION] + noresume Don't check if there's a hibernation image + present during boot. + nocompress Don't compress/decompress hibernation images. + retain_initrd [RAM] Keep initrd memory after extraction rhash_entries= [KNL,NET] diff --git a/Documentation/power/swsusp.txt b/Documentation/power/swsusp.txt index 9d60ab717a7b..ea718891a665 100644 --- a/Documentation/power/swsusp.txt +++ b/Documentation/power/swsusp.txt @@ -66,7 +66,8 @@ swsusp saves the state of the machine into active swaps and then reboots or powerdowns. You must explicitly specify the swap partition to resume from with ``resume='' kernel option. If signature is found it loads and restores saved state. If the option ``noresume'' is specified as a boot parameter, it skips -the resuming. +the resuming. If the option ``hibernate=nocompress'' is specified as a boot +parameter, it saves hibernation image without compression. In the meantime while the system is suspended you should not add/remove any of the hardware, write to the filesystems, etc. diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index ca6066a6952e..cb57eb99215f 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -137,6 +137,8 @@ config SUSPEND_FREEZER config HIBERNATION bool "Hibernation (aka 'suspend to disk')" depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE + select LZO_COMPRESS + select LZO_DECOMPRESS select SUSPEND_NVS if HAS_IOMEM ---help--- Enable the suspend to disk (STD) functionality, which is usually diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 8dc31e02ae12..6c9c9dc48c75 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -29,6 +29,7 @@ #include "power.h" +static int nocompress = 0; static int noresume = 0; static char resume_file[256] = CONFIG_PM_STD_PARTITION; dev_t swsusp_resume_device; @@ -638,6 +639,8 @@ int hibernate(void) if (hibernation_mode == HIBERNATION_PLATFORM) flags |= SF_PLATFORM_MODE; + if (nocompress) + flags |= SF_NOCOMPRESS_MODE; pr_debug("PM: writing image.\n"); error = swsusp_write(flags); swsusp_free(); @@ -1004,6 +1007,15 @@ static int __init resume_offset_setup(char *str) return 1; } +static int __init hibernate_setup(char *str) +{ + if (!strncmp(str, "noresume", 8)) + noresume = 1; + else if (!strncmp(str, "nocompress", 10)) + nocompress = 1; + return 1; +} + static int __init noresume_setup(char *str) { noresume = 1; @@ -1013,3 +1025,4 @@ static int __init noresume_setup(char *str) __setup("noresume", noresume_setup); __setup("resume_offset=", resume_offset_setup); __setup("resume=", resume_setup); +__setup("hibernate=", hibernate_setup); diff --git a/kernel/power/power.h b/kernel/power/power.h index 006270fe382d..c7e42e47eb0b 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -134,6 +134,7 @@ extern int swsusp_swap_in_use(void); * the image header. */ #define SF_PLATFORM_MODE 1 +#define SF_NOCOMPRESS_MODE 2 /* kernel/power/hibernate.c */ extern int swsusp_check(void); diff --git a/kernel/power/swap.c b/kernel/power/swap.c index e6a5bdf61a37..3dc0552cddfb 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "power.h" @@ -357,6 +359,18 @@ static int swap_writer_finish(struct swap_map_handle *handle, return error; } +/* We need to remember how much compressed data we need to read. */ +#define LZO_HEADER sizeof(size_t) + +/* Number of pages/bytes we'll compress at one time. */ +#define LZO_UNC_PAGES 32 +#define LZO_UNC_SIZE (LZO_UNC_PAGES * PAGE_SIZE) + +/* Number of pages/bytes we need for compressed data (worst case). */ +#define LZO_CMP_PAGES DIV_ROUND_UP(lzo1x_worst_compress(LZO_UNC_SIZE) + \ + LZO_HEADER, PAGE_SIZE) +#define LZO_CMP_SIZE (LZO_CMP_PAGES * PAGE_SIZE) + /** * save_image - save the suspend image data */ @@ -404,6 +418,137 @@ static int save_image(struct swap_map_handle *handle, return ret; } + +/** + * save_image_lzo - Save the suspend image data compressed with LZO. + * @handle: Swap mam handle to use for saving the image. + * @snapshot: Image to read data from. + * @nr_to_write: Number of pages to save. + */ +static int save_image_lzo(struct swap_map_handle *handle, + struct snapshot_handle *snapshot, + unsigned int nr_to_write) +{ + unsigned int m; + int ret = 0; + int nr_pages; + int err2; + struct bio *bio; + struct timeval start; + struct timeval stop; + size_t off, unc_len, cmp_len; + unsigned char *unc, *cmp, *wrk, *page; + + page = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH); + if (!page) { + printk(KERN_ERR "PM: Failed to allocate LZO page\n"); + return -ENOMEM; + } + + wrk = vmalloc(LZO1X_1_MEM_COMPRESS); + if (!wrk) { + printk(KERN_ERR "PM: Failed to allocate LZO workspace\n"); + free_page((unsigned long)page); + return -ENOMEM; + } + + unc = vmalloc(LZO_UNC_SIZE); + if (!unc) { + printk(KERN_ERR "PM: Failed to allocate LZO uncompressed\n"); + vfree(wrk); + free_page((unsigned long)page); + return -ENOMEM; + } + + cmp = vmalloc(LZO_CMP_SIZE); + if (!cmp) { + printk(KERN_ERR "PM: Failed to allocate LZO compressed\n"); + vfree(unc); + vfree(wrk); + free_page((unsigned long)page); + return -ENOMEM; + } + + printk(KERN_INFO + "PM: Compressing and saving image data (%u pages) ... ", + nr_to_write); + m = nr_to_write / 100; + if (!m) + m = 1; + nr_pages = 0; + bio = NULL; + do_gettimeofday(&start); + for (;;) { + for (off = 0; off < LZO_UNC_SIZE; off += PAGE_SIZE) { + ret = snapshot_read_next(snapshot); + if (ret < 0) + goto out_finish; + + if (!ret) + break; + + memcpy(unc + off, data_of(*snapshot), PAGE_SIZE); + + if (!(nr_pages % m)) + printk(KERN_CONT "\b\b\b\b%3d%%", nr_pages / m); + nr_pages++; + } + + if (!off) + break; + + unc_len = off; + ret = lzo1x_1_compress(unc, unc_len, + cmp + LZO_HEADER, &cmp_len, wrk); + if (ret < 0) { + printk(KERN_ERR "PM: LZO compression failed\n"); + break; + } + + if (unlikely(!cmp_len || + cmp_len > lzo1x_worst_compress(unc_len))) { + printk(KERN_ERR "PM: Invalid LZO compressed length\n"); + ret = -1; + break; + } + + *(size_t *)cmp = cmp_len; + + /* + * Given we are writing one page at a time to disk, we copy + * that much from the buffer, although the last bit will likely + * be smaller than full page. This is OK - we saved the length + * of the compressed data, so any garbage at the end will be + * discarded when we read it. + */ + for (off = 0; off < LZO_HEADER + cmp_len; off += PAGE_SIZE) { + memcpy(page, cmp + off, PAGE_SIZE); + + ret = swap_write_page(handle, page, &bio); + if (ret) + goto out_finish; + } + } + +out_finish: + err2 = hib_wait_on_bio_chain(&bio); + do_gettimeofday(&stop); + if (!ret) + ret = err2; + if (!ret) + printk(KERN_CONT "\b\b\b\bdone\n"); + else + printk(KERN_CONT "\n"); + swsusp_show_speed(&start, &stop, nr_to_write, "Wrote"); + + vfree(cmp); + vfree(unc); + vfree(wrk); + free_page((unsigned long)page); + + return ret; +} + /** * enough_swap - Make sure we have enough swap to save the image. * @@ -411,12 +556,16 @@ static int save_image(struct swap_map_handle *handle, * space avaiable from the resume partition. */ -static int enough_swap(unsigned int nr_pages) +static int enough_swap(unsigned int nr_pages, unsigned int flags) { unsigned int free_swap = count_swap_pages(root_swap, 1); + unsigned int required; pr_debug("PM: Free swap pages: %u\n", free_swap); - return free_swap > nr_pages + PAGES_FOR_IO; + + required = PAGES_FOR_IO + ((flags & SF_NOCOMPRESS_MODE) ? + nr_pages : (nr_pages * LZO_CMP_PAGES) / LZO_UNC_PAGES + 1); + return free_swap > required; } /** @@ -443,7 +592,7 @@ int swsusp_write(unsigned int flags) printk(KERN_ERR "PM: Cannot get swap writer\n"); return error; } - if (!enough_swap(pages)) { + if (!enough_swap(pages, flags)) { printk(KERN_ERR "PM: Not enough free swap\n"); error = -ENOSPC; goto out_finish; @@ -458,8 +607,11 @@ int swsusp_write(unsigned int flags) } header = (struct swsusp_info *)data_of(snapshot); error = swap_write_page(&handle, header, NULL); - if (!error) - error = save_image(&handle, &snapshot, pages - 1); + if (!error) { + error = (flags & SF_NOCOMPRESS_MODE) ? + save_image(&handle, &snapshot, pages - 1) : + save_image_lzo(&handle, &snapshot, pages - 1); + } out_finish: error = swap_writer_finish(&handle, flags, error); return error; @@ -589,6 +741,127 @@ static int load_image(struct swap_map_handle *handle, return error; } +/** + * load_image_lzo - Load compressed image data and decompress them with LZO. + * @handle: Swap map handle to use for loading data. + * @snapshot: Image to copy uncompressed data into. + * @nr_to_read: Number of pages to load. + */ +static int load_image_lzo(struct swap_map_handle *handle, + struct snapshot_handle *snapshot, + unsigned int nr_to_read) +{ + unsigned int m; + int error = 0; + struct timeval start; + struct timeval stop; + unsigned nr_pages; + size_t off, unc_len, cmp_len; + unsigned char *unc, *cmp, *page; + + page = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH); + if (!page) { + printk(KERN_ERR "PM: Failed to allocate LZO page\n"); + return -ENOMEM; + } + + unc = vmalloc(LZO_UNC_SIZE); + if (!unc) { + printk(KERN_ERR "PM: Failed to allocate LZO uncompressed\n"); + free_page((unsigned long)page); + return -ENOMEM; + } + + cmp = vmalloc(LZO_CMP_SIZE); + if (!cmp) { + printk(KERN_ERR "PM: Failed to allocate LZO compressed\n"); + vfree(unc); + free_page((unsigned long)page); + return -ENOMEM; + } + + printk(KERN_INFO + "PM: Loading and decompressing image data (%u pages) ... ", + nr_to_read); + m = nr_to_read / 100; + if (!m) + m = 1; + nr_pages = 0; + do_gettimeofday(&start); + + error = snapshot_write_next(snapshot); + if (error <= 0) + goto out_finish; + + for (;;) { + error = swap_read_page(handle, page, NULL); /* sync */ + if (error) + break; + + cmp_len = *(size_t *)page; + if (unlikely(!cmp_len || + cmp_len > lzo1x_worst_compress(LZO_UNC_SIZE))) { + printk(KERN_ERR "PM: Invalid LZO compressed length\n"); + error = -1; + break; + } + + memcpy(cmp, page, PAGE_SIZE); + for (off = PAGE_SIZE; off < LZO_HEADER + cmp_len; off += PAGE_SIZE) { + error = swap_read_page(handle, page, NULL); /* sync */ + if (error) + goto out_finish; + + memcpy(cmp + off, page, PAGE_SIZE); + } + + unc_len = LZO_UNC_SIZE; + error = lzo1x_decompress_safe(cmp + LZO_HEADER, cmp_len, + unc, &unc_len); + if (error < 0) { + printk(KERN_ERR "PM: LZO decompression failed\n"); + break; + } + + if (unlikely(!unc_len || + unc_len > LZO_UNC_SIZE || + unc_len & (PAGE_SIZE - 1))) { + printk(KERN_ERR "PM: Invalid LZO uncompressed length\n"); + error = -1; + break; + } + + for (off = 0; off < unc_len; off += PAGE_SIZE) { + memcpy(data_of(*snapshot), unc + off, PAGE_SIZE); + + if (!(nr_pages % m)) + printk("\b\b\b\b%3d%%", nr_pages / m); + nr_pages++; + + error = snapshot_write_next(snapshot); + if (error <= 0) + goto out_finish; + } + } + +out_finish: + do_gettimeofday(&stop); + if (!error) { + printk("\b\b\b\bdone\n"); + snapshot_write_finalize(snapshot); + if (!snapshot_image_loaded(snapshot)) + error = -ENODATA; + } else + printk("\n"); + swsusp_show_speed(&start, &stop, nr_to_read, "Read"); + + vfree(cmp); + vfree(unc); + free_page((unsigned long)page); + + return error; +} + /** * swsusp_read - read the hibernation image. * @flags_p: flags passed by the "frozen" kernel in the image header should @@ -612,8 +885,11 @@ int swsusp_read(unsigned int *flags_p) goto end; if (!error) error = swap_read_page(&handle, header, NULL); - if (!error) - error = load_image(&handle, &snapshot, header->pages - 1); + if (!error) { + error = (*flags_p & SF_NOCOMPRESS_MODE) ? + load_image(&handle, &snapshot, header->pages - 1) : + load_image_lzo(&handle, &snapshot, header->pages - 1); + } swap_reader_finish(&handle); end: if (!error) -- cgit v1.2.3-70-g09d2 From ac5c24ec1e983313ef0015258fba6f630e54e7cf Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 20 Sep 2010 19:44:56 +0200 Subject: PM / Hibernate: Make default image size depend on total RAM size The default hibernation image size is currently hard coded and euqal to 500 MB, which is not a reasonable default on many contemporary systems. Make it equal 2/5 of the total RAM size (this is slightly below the maximum, i.e. 1/2 of the total RAM size, and seems to be generally suitable). Signed-off-by: Rafael J. Wysocki Tested-by: M. Vefa Bicakci --- Documentation/power/interface.txt | 2 +- kernel/power/main.c | 1 + kernel/power/power.h | 9 ++++++++- kernel/power/snapshot.c | 7 ++++++- 4 files changed, 16 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/power/interface.txt b/Documentation/power/interface.txt index e67211fe0ee2..c537834af005 100644 --- a/Documentation/power/interface.txt +++ b/Documentation/power/interface.txt @@ -57,7 +57,7 @@ smallest image possible. In particular, if "0" is written to this file, the suspend image will be as small as possible. Reading from this file will display the current image size limit, which -is set to 500 MB by default. +is set to 2/5 of available RAM by default. /sys/power/pm_trace controls the code which saves the last PM event point in the RTC across reboots, so that you can debug a machine that just hangs diff --git a/kernel/power/main.c b/kernel/power/main.c index 0a28d4db3597..f06ad6eff37a 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -321,6 +321,7 @@ static int __init pm_init(void) int error = pm_start_workqueue(); if (error) return error; + hibernate_image_size_init(); power_kobj = kobject_create_and_add("power", NULL); if (!power_kobj) return -ENOMEM; diff --git a/kernel/power/power.h b/kernel/power/power.h index c7e42e47eb0b..03634be55f62 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -14,6 +14,9 @@ struct swsusp_info { } __attribute__((aligned(PAGE_SIZE))); #ifdef CONFIG_HIBERNATION +/* kernel/power/snapshot.c */ +extern void __init hibernate_image_size_init(void); + #ifdef CONFIG_ARCH_HIBERNATION_HEADER /* Maximum size of architecture specific data in a hibernation header */ #define MAX_ARCH_HEADER_SIZE (sizeof(struct new_utsname) + 4) @@ -49,7 +52,11 @@ static inline char *check_image_kernel(struct swsusp_info *info) extern int hibernation_snapshot(int platform_mode); extern int hibernation_restore(int platform_mode); extern int hibernation_platform_enter(void); -#endif + +#else /* !CONFIG_HIBERNATION */ + +static inline void hibernate_image_size_init(void) {} +#endif /* !CONFIG_HIBERNATION */ extern int pfn_is_nosave(unsigned long); diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index d9191b40cf6e..ac7eb109f196 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -46,7 +46,12 @@ static void swsusp_unset_page_forbidden(struct page *); * size will not exceed N bytes, but if that is impossible, it will * try to create the smallest image possible. */ -unsigned long image_size = 500 * 1024 * 1024; +unsigned long image_size; + +void __init hibernate_image_size_init(void) +{ + image_size = ((totalram_pages * 2) / 5) * PAGE_SIZE; +} /* List of PBEs needed for restoring the pages that were allocated before * the suspend and included in the suspend image, but have also been -- cgit v1.2.3-70-g09d2 From 074037ec79bea73edf1b1ec72fef1010e83e3cc5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 22 Sep 2010 22:09:10 +0200 Subject: PM / Wakeup: Introduce wakeup source objects and event statistics (v3) Introduce struct wakeup_source for representing system wakeup sources within the kernel and for collecting statistics related to them. Make the recently introduced helper functions pm_wakeup_event(), pm_stay_awake() and pm_relax() use struct wakeup_source objects internally, so that wakeup statistics associated with wakeup devices can be collected and reported in a consistent way (the definition of pm_relax() is changed, which is harmless, because this function is not called directly by anyone yet). Introduce new wakeup-related sysfs device attributes in /sys/devices/.../power for reporting the device wakeup statistics. Change the global wakeup events counters event_count and events_in_progress into atomic variables, so that it is not necessary to acquire a global spinlock in pm_wakeup_event(), pm_stay_awake() and pm_relax(), which should allow us to avoid lock contention in these functions on SMP systems with many wakeup devices. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-devices-power | 70 ++++ drivers/base/power/main.c | 4 +- drivers/base/power/power.h | 1 + drivers/base/power/runtime.c | 2 - drivers/base/power/sysfs.c | 121 +++++- drivers/base/power/wakeup.c | 528 ++++++++++++++++++++++---- include/linux/pm.h | 16 +- include/linux/pm_wakeup.h | 127 +++++-- include/linux/suspend.h | 4 +- kernel/power/main.c | 8 +- 10 files changed, 756 insertions(+), 125 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index 6123c523bfd7..6bb2dd3c3a71 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power @@ -77,3 +77,73 @@ Description: devices this attribute is set to "enabled" by bus type code or device drivers and in that cases it should be safe to leave the default value. + +What: /sys/devices/.../power/wakeup_count +Date: September 2010 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../wakeup_count attribute contains the number + of signaled wakeup events associated with the device. This + attribute is read-only. If the device is not enabled to wake up + the system from sleep states, this attribute is empty. + +What: /sys/devices/.../power/wakeup_active_count +Date: September 2010 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../wakeup_active_count attribute contains the + number of times the processing of wakeup events associated with + the device was completed (at the kernel level). This attribute + is read-only. If the device is not enabled to wake up the + system from sleep states, this attribute is empty. + +What: /sys/devices/.../power/wakeup_hit_count +Date: September 2010 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../wakeup_hit_count attribute contains the + number of times the processing of a wakeup event associated with + the device might prevent the system from entering a sleep state. + This attribute is read-only. If the device is not enabled to + wake up the system from sleep states, this attribute is empty. + +What: /sys/devices/.../power/wakeup_active +Date: September 2010 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../wakeup_active attribute contains either 1, + or 0, depending on whether or not a wakeup event associated with + the device is being processed (1). This attribute is read-only. + If the device is not enabled to wake up the system from sleep + states, this attribute is empty. + +What: /sys/devices/.../power/wakeup_total_time_ms +Date: September 2010 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../wakeup_total_time_ms attribute contains + the total time of processing wakeup events associated with the + device, in milliseconds. This attribute is read-only. If the + device is not enabled to wake up the system from sleep states, + this attribute is empty. + +What: /sys/devices/.../power/wakeup_max_time_ms +Date: September 2010 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../wakeup_max_time_ms attribute contains + the maximum time of processing a single wakeup event associated + with the device, in milliseconds. This attribute is read-only. + If the device is not enabled to wake up the system from sleep + states, this attribute is empty. + +What: /sys/devices/.../power/wakeup_last_time_ms +Date: September 2010 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../wakeup_last_time_ms attribute contains + the value of the monotonic clock corresponding to the time of + signaling the last wakeup event associated with the device, in + milliseconds. This attribute is read-only. If the device is + not enabled to wake up the system from sleep states, this + attribute is empty. diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index db677199403c..7ae6fe414c38 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -60,7 +60,8 @@ void device_pm_init(struct device *dev) dev->power.status = DPM_ON; init_completion(&dev->power.completion); complete_all(&dev->power.completion); - dev->power.wakeup_count = 0; + dev->power.wakeup = NULL; + spin_lock_init(&dev->power.lock); pm_runtime_init(dev); } @@ -120,6 +121,7 @@ void device_pm_remove(struct device *dev) mutex_lock(&dpm_list_mtx); list_del_init(&dev->power.entry); mutex_unlock(&dpm_list_mtx); + device_wakeup_disable(dev); pm_runtime_remove(dev); } diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index c0bd03c83b9c..8b2745c69e2a 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -34,6 +34,7 @@ extern void device_pm_move_last(struct device *); static inline void device_pm_init(struct device *dev) { + spin_lock_init(&dev->power.lock); pm_runtime_init(dev); } diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index b78c401ffa73..e9520ad2d716 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -1099,8 +1099,6 @@ EXPORT_SYMBOL_GPL(pm_runtime_allow); */ void pm_runtime_init(struct device *dev) { - spin_lock_init(&dev->power.lock); - dev->power.runtime_status = RPM_SUSPENDED; dev->power.idle_notification = false; diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index e56b4388fe61..8859780817e1 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -210,11 +210,122 @@ static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); static ssize_t wakeup_count_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", dev->power.wakeup_count); + unsigned long count = 0; + bool enabled = false; + + spin_lock_irq(&dev->power.lock); + if (dev->power.wakeup) { + count = dev->power.wakeup->event_count; + enabled = true; + } + spin_unlock_irq(&dev->power.lock); + return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n"); } static DEVICE_ATTR(wakeup_count, 0444, wakeup_count_show, NULL); -#endif + +static ssize_t wakeup_active_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long count = 0; + bool enabled = false; + + spin_lock_irq(&dev->power.lock); + if (dev->power.wakeup) { + count = dev->power.wakeup->active_count; + enabled = true; + } + spin_unlock_irq(&dev->power.lock); + return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n"); +} + +static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL); + +static ssize_t wakeup_hit_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long count = 0; + bool enabled = false; + + spin_lock_irq(&dev->power.lock); + if (dev->power.wakeup) { + count = dev->power.wakeup->hit_count; + enabled = true; + } + spin_unlock_irq(&dev->power.lock); + return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n"); +} + +static DEVICE_ATTR(wakeup_hit_count, 0444, wakeup_hit_count_show, NULL); + +static ssize_t wakeup_active_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int active = 0; + bool enabled = false; + + spin_lock_irq(&dev->power.lock); + if (dev->power.wakeup) { + active = dev->power.wakeup->active; + enabled = true; + } + spin_unlock_irq(&dev->power.lock); + return enabled ? sprintf(buf, "%u\n", active) : sprintf(buf, "\n"); +} + +static DEVICE_ATTR(wakeup_active, 0444, wakeup_active_show, NULL); + +static ssize_t wakeup_total_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + s64 msec = 0; + bool enabled = false; + + spin_lock_irq(&dev->power.lock); + if (dev->power.wakeup) { + msec = ktime_to_ms(dev->power.wakeup->total_time); + enabled = true; + } + spin_unlock_irq(&dev->power.lock); + return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n"); +} + +static DEVICE_ATTR(wakeup_total_time_ms, 0444, wakeup_total_time_show, NULL); + +static ssize_t wakeup_max_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + s64 msec = 0; + bool enabled = false; + + spin_lock_irq(&dev->power.lock); + if (dev->power.wakeup) { + msec = ktime_to_ms(dev->power.wakeup->max_time); + enabled = true; + } + spin_unlock_irq(&dev->power.lock); + return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n"); +} + +static DEVICE_ATTR(wakeup_max_time_ms, 0444, wakeup_max_time_show, NULL); + +static ssize_t wakeup_last_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + s64 msec = 0; + bool enabled = false; + + spin_lock_irq(&dev->power.lock); + if (dev->power.wakeup) { + msec = ktime_to_ms(dev->power.wakeup->last_time); + enabled = true; + } + spin_unlock_irq(&dev->power.lock); + return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n"); +} + +static DEVICE_ATTR(wakeup_last_time_ms, 0444, wakeup_last_time_show, NULL); +#endif /* CONFIG_PM_SLEEP */ #ifdef CONFIG_PM_ADVANCED_DEBUG #ifdef CONFIG_PM_RUNTIME @@ -288,6 +399,12 @@ static struct attribute * power_attrs[] = { &dev_attr_wakeup.attr, #ifdef CONFIG_PM_SLEEP &dev_attr_wakeup_count.attr, + &dev_attr_wakeup_active_count.attr, + &dev_attr_wakeup_hit_count.attr, + &dev_attr_wakeup_active.attr, + &dev_attr_wakeup_total_time_ms.attr, + &dev_attr_wakeup_max_time_ms.attr, + &dev_attr_wakeup_last_time_ms.attr, #endif #ifdef CONFIG_PM_ADVANCED_DEBUG &dev_attr_async.attr, diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index eb594facfc3f..03751a01dbad 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -11,7 +11,10 @@ #include #include #include -#include + +#include "power.h" + +#define TIMEOUT 100 /* * If set, the suspend/hibernate code will abort transitions to a sleep state @@ -20,18 +23,244 @@ bool events_check_enabled; /* The counter of registered wakeup events. */ -static unsigned long event_count; +static atomic_t event_count = ATOMIC_INIT(0); /* A preserved old value of event_count. */ -static unsigned long saved_event_count; +static unsigned int saved_count; /* The counter of wakeup events being processed. */ -static unsigned long events_in_progress; +static atomic_t events_in_progress = ATOMIC_INIT(0); static DEFINE_SPINLOCK(events_lock); static void pm_wakeup_timer_fn(unsigned long data); -static DEFINE_TIMER(events_timer, pm_wakeup_timer_fn, 0, 0); -static unsigned long events_timer_expires; +static LIST_HEAD(wakeup_sources); + +/** + * wakeup_source_create - Create a struct wakeup_source object. + * @name: Name of the new wakeup source. + */ +struct wakeup_source *wakeup_source_create(const char *name) +{ + struct wakeup_source *ws; + + ws = kzalloc(sizeof(*ws), GFP_KERNEL); + if (!ws) + return NULL; + + spin_lock_init(&ws->lock); + if (name) + ws->name = kstrdup(name, GFP_KERNEL); + + return ws; +} +EXPORT_SYMBOL_GPL(wakeup_source_create); + +/** + * wakeup_source_destroy - Destroy a struct wakeup_source object. + * @ws: Wakeup source to destroy. + */ +void wakeup_source_destroy(struct wakeup_source *ws) +{ + if (!ws) + return; + + spin_lock_irq(&ws->lock); + while (ws->active) { + spin_unlock_irq(&ws->lock); + + schedule_timeout_interruptible(msecs_to_jiffies(TIMEOUT)); + + spin_lock_irq(&ws->lock); + } + spin_unlock_irq(&ws->lock); + + kfree(ws->name); + kfree(ws); +} +EXPORT_SYMBOL_GPL(wakeup_source_destroy); + +/** + * wakeup_source_add - Add given object to the list of wakeup sources. + * @ws: Wakeup source object to add to the list. + */ +void wakeup_source_add(struct wakeup_source *ws) +{ + if (WARN_ON(!ws)) + return; + + setup_timer(&ws->timer, pm_wakeup_timer_fn, (unsigned long)ws); + ws->active = false; + + spin_lock_irq(&events_lock); + list_add_rcu(&ws->entry, &wakeup_sources); + spin_unlock_irq(&events_lock); + synchronize_rcu(); +} +EXPORT_SYMBOL_GPL(wakeup_source_add); + +/** + * wakeup_source_remove - Remove given object from the wakeup sources list. + * @ws: Wakeup source object to remove from the list. + */ +void wakeup_source_remove(struct wakeup_source *ws) +{ + if (WARN_ON(!ws)) + return; + + spin_lock_irq(&events_lock); + list_del_rcu(&ws->entry); + spin_unlock_irq(&events_lock); + synchronize_rcu(); +} +EXPORT_SYMBOL_GPL(wakeup_source_remove); + +/** + * wakeup_source_register - Create wakeup source and add it to the list. + * @name: Name of the wakeup source to register. + */ +struct wakeup_source *wakeup_source_register(const char *name) +{ + struct wakeup_source *ws; + + ws = wakeup_source_create(name); + if (ws) + wakeup_source_add(ws); + + return ws; +} +EXPORT_SYMBOL_GPL(wakeup_source_register); + +/** + * wakeup_source_unregister - Remove wakeup source from the list and remove it. + * @ws: Wakeup source object to unregister. + */ +void wakeup_source_unregister(struct wakeup_source *ws) +{ + wakeup_source_remove(ws); + wakeup_source_destroy(ws); +} +EXPORT_SYMBOL_GPL(wakeup_source_unregister); + +/** + * device_wakeup_attach - Attach a wakeup source object to a device object. + * @dev: Device to handle. + * @ws: Wakeup source object to attach to @dev. + * + * This causes @dev to be treated as a wakeup device. + */ +static int device_wakeup_attach(struct device *dev, struct wakeup_source *ws) +{ + spin_lock_irq(&dev->power.lock); + if (dev->power.wakeup) { + spin_unlock_irq(&dev->power.lock); + return -EEXIST; + } + dev->power.wakeup = ws; + spin_unlock_irq(&dev->power.lock); + return 0; +} + +/** + * device_wakeup_enable - Enable given device to be a wakeup source. + * @dev: Device to handle. + * + * Create a wakeup source object, register it and attach it to @dev. + */ +int device_wakeup_enable(struct device *dev) +{ + struct wakeup_source *ws; + int ret; + + if (!dev || !dev->power.can_wakeup) + return -EINVAL; + + ws = wakeup_source_register(dev_name(dev)); + if (!ws) + return -ENOMEM; + + ret = device_wakeup_attach(dev, ws); + if (ret) + wakeup_source_unregister(ws); + + return ret; +} +EXPORT_SYMBOL_GPL(device_wakeup_enable); + +/** + * device_wakeup_detach - Detach a device's wakeup source object from it. + * @dev: Device to detach the wakeup source object from. + * + * After it returns, @dev will not be treated as a wakeup device any more. + */ +static struct wakeup_source *device_wakeup_detach(struct device *dev) +{ + struct wakeup_source *ws; + + spin_lock_irq(&dev->power.lock); + ws = dev->power.wakeup; + dev->power.wakeup = NULL; + spin_unlock_irq(&dev->power.lock); + return ws; +} + +/** + * device_wakeup_disable - Do not regard a device as a wakeup source any more. + * @dev: Device to handle. + * + * Detach the @dev's wakeup source object from it, unregister this wakeup source + * object and destroy it. + */ +int device_wakeup_disable(struct device *dev) +{ + struct wakeup_source *ws; + + if (!dev || !dev->power.can_wakeup) + return -EINVAL; + + ws = device_wakeup_detach(dev); + if (ws) + wakeup_source_unregister(ws); + + return 0; +} +EXPORT_SYMBOL_GPL(device_wakeup_disable); + +/** + * device_init_wakeup - Device wakeup initialization. + * @dev: Device to handle. + * @enable: Whether or not to enable @dev as a wakeup device. + * + * By default, most devices should leave wakeup disabled. The exceptions are + * devices that everyone expects to be wakeup sources: keyboards, power buttons, + * possibly network interfaces, etc. + */ +int device_init_wakeup(struct device *dev, bool enable) +{ + int ret = 0; + + if (enable) { + device_set_wakeup_capable(dev, true); + ret = device_wakeup_enable(dev); + } else { + device_set_wakeup_capable(dev, false); + } + + return ret; +} +EXPORT_SYMBOL_GPL(device_init_wakeup); + +/** + * device_set_wakeup_enable - Enable or disable a device to wake up the system. + * @dev: Device to handle. + */ +int device_set_wakeup_enable(struct device *dev, bool enable) +{ + if (!dev || !dev->power.can_wakeup) + return -EINVAL; + + return enable ? device_wakeup_enable(dev) : device_wakeup_disable(dev); +} +EXPORT_SYMBOL_GPL(device_set_wakeup_enable); /* * The functions below use the observation that each wakeup event starts a @@ -55,118 +284,259 @@ static unsigned long events_timer_expires; * knowledge, however, may not be available to it, so it can simply specify time * to wait before the system can be suspended and pass it as the second * argument of pm_wakeup_event(). + * + * It is valid to call pm_relax() after pm_wakeup_event(), in which case the + * "no suspend" period will be ended either by the pm_relax(), or by the timer + * function executed when the timer expires, whichever comes first. */ +/** + * wakup_source_activate - Mark given wakeup source as active. + * @ws: Wakeup source to handle. + * + * Update the @ws' statistics and, if @ws has just been activated, notify the PM + * core of the event by incrementing the counter of of wakeup events being + * processed. + */ +static void wakeup_source_activate(struct wakeup_source *ws) +{ + ws->active = true; + ws->active_count++; + ws->timer_expires = jiffies; + ws->last_time = ktime_get(); + + atomic_inc(&events_in_progress); +} + +/** + * __pm_stay_awake - Notify the PM core of a wakeup event. + * @ws: Wakeup source object associated with the source of the event. + * + * It is safe to call this function from interrupt context. + */ +void __pm_stay_awake(struct wakeup_source *ws) +{ + unsigned long flags; + + if (!ws) + return; + + spin_lock_irqsave(&ws->lock, flags); + ws->event_count++; + if (!ws->active) + wakeup_source_activate(ws); + spin_unlock_irqrestore(&ws->lock, flags); +} +EXPORT_SYMBOL_GPL(__pm_stay_awake); + /** * pm_stay_awake - Notify the PM core that a wakeup event is being processed. * @dev: Device the wakeup event is related to. * - * Notify the PM core of a wakeup event (signaled by @dev) by incrementing the - * counter of wakeup events being processed. If @dev is not NULL, the counter - * of wakeup events related to @dev is incremented too. + * Notify the PM core of a wakeup event (signaled by @dev) by calling + * __pm_stay_awake for the @dev's wakeup source object. * * Call this function after detecting of a wakeup event if pm_relax() is going * to be called directly after processing the event (and possibly passing it to * user space for further processing). - * - * It is safe to call this function from interrupt context. */ void pm_stay_awake(struct device *dev) { unsigned long flags; - spin_lock_irqsave(&events_lock, flags); - if (dev) - dev->power.wakeup_count++; + if (!dev) + return; - events_in_progress++; - spin_unlock_irqrestore(&events_lock, flags); + spin_lock_irqsave(&dev->power.lock, flags); + __pm_stay_awake(dev->power.wakeup); + spin_unlock_irqrestore(&dev->power.lock, flags); } +EXPORT_SYMBOL_GPL(pm_stay_awake); /** - * pm_relax - Notify the PM core that processing of a wakeup event has ended. + * wakup_source_deactivate - Mark given wakeup source as inactive. + * @ws: Wakeup source to handle. * - * Notify the PM core that a wakeup event has been processed by decrementing - * the counter of wakeup events being processed and incrementing the counter - * of registered wakeup events. + * Update the @ws' statistics and notify the PM core that the wakeup source has + * become inactive by decrementing the counter of wakeup events being processed + * and incrementing the counter of registered wakeup events. + */ +static void wakeup_source_deactivate(struct wakeup_source *ws) +{ + ktime_t duration; + ktime_t now; + + ws->relax_count++; + /* + * __pm_relax() may be called directly or from a timer function. + * If it is called directly right after the timer function has been + * started, but before the timer function calls __pm_relax(), it is + * possible that __pm_stay_awake() will be called in the meantime and + * will set ws->active. Then, ws->active may be cleared immediately + * by the __pm_relax() called from the timer function, but in such a + * case ws->relax_count will be different from ws->active_count. + */ + if (ws->relax_count != ws->active_count) { + ws->relax_count--; + return; + } + + ws->active = false; + + now = ktime_get(); + duration = ktime_sub(now, ws->last_time); + ws->total_time = ktime_add(ws->total_time, duration); + if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time)) + ws->max_time = duration; + + del_timer(&ws->timer); + + /* + * event_count has to be incremented before events_in_progress is + * modified, so that the callers of pm_check_wakeup_events() and + * pm_save_wakeup_count() don't see the old value of event_count and + * events_in_progress equal to zero at the same time. + */ + atomic_inc(&event_count); + smp_mb__before_atomic_dec(); + atomic_dec(&events_in_progress); +} + +/** + * __pm_relax - Notify the PM core that processing of a wakeup event has ended. + * @ws: Wakeup source object associated with the source of the event. * * Call this function for wakeup events whose processing started with calling - * pm_stay_awake(). + * __pm_stay_awake(). * * It is safe to call it from interrupt context. */ -void pm_relax(void) +void __pm_relax(struct wakeup_source *ws) { unsigned long flags; - spin_lock_irqsave(&events_lock, flags); - if (events_in_progress) { - events_in_progress--; - event_count++; - } - spin_unlock_irqrestore(&events_lock, flags); + if (!ws) + return; + + spin_lock_irqsave(&ws->lock, flags); + if (ws->active) + wakeup_source_deactivate(ws); + spin_unlock_irqrestore(&ws->lock, flags); +} +EXPORT_SYMBOL_GPL(__pm_relax); + +/** + * pm_relax - Notify the PM core that processing of a wakeup event has ended. + * @dev: Device that signaled the event. + * + * Execute __pm_relax() for the @dev's wakeup source object. + */ +void pm_relax(struct device *dev) +{ + unsigned long flags; + + if (!dev) + return; + + spin_lock_irqsave(&dev->power.lock, flags); + __pm_relax(dev->power.wakeup); + spin_unlock_irqrestore(&dev->power.lock, flags); } +EXPORT_SYMBOL_GPL(pm_relax); /** * pm_wakeup_timer_fn - Delayed finalization of a wakeup event. + * @data: Address of the wakeup source object associated with the event source. * - * Decrease the counter of wakeup events being processed after it was increased - * by pm_wakeup_event(). + * Call __pm_relax() for the wakeup source whose address is stored in @data. */ static void pm_wakeup_timer_fn(unsigned long data) +{ + __pm_relax((struct wakeup_source *)data); +} + +/** + * __pm_wakeup_event - Notify the PM core of a wakeup event. + * @ws: Wakeup source object associated with the event source. + * @msec: Anticipated event processing time (in milliseconds). + * + * Notify the PM core of a wakeup event whose source is @ws that will take + * approximately @msec milliseconds to be processed by the kernel. If @ws is + * not active, activate it. If @msec is nonzero, set up the @ws' timer to + * execute pm_wakeup_timer_fn() in future. + * + * It is safe to call this function from interrupt context. + */ +void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec) { unsigned long flags; + unsigned long expires; - spin_lock_irqsave(&events_lock, flags); - if (events_timer_expires - && time_before_eq(events_timer_expires, jiffies)) { - events_in_progress--; - events_timer_expires = 0; + if (!ws) + return; + + spin_lock_irqsave(&ws->lock, flags); + + ws->event_count++; + if (!ws->active) + wakeup_source_activate(ws); + + if (!msec) { + wakeup_source_deactivate(ws); + goto unlock; } - spin_unlock_irqrestore(&events_lock, flags); + + expires = jiffies + msecs_to_jiffies(msec); + if (!expires) + expires = 1; + + if (time_after(expires, ws->timer_expires)) { + mod_timer(&ws->timer, expires); + ws->timer_expires = expires; + } + + unlock: + spin_unlock_irqrestore(&ws->lock, flags); } +EXPORT_SYMBOL_GPL(__pm_wakeup_event); + /** * pm_wakeup_event - Notify the PM core of a wakeup event. * @dev: Device the wakeup event is related to. * @msec: Anticipated event processing time (in milliseconds). * - * Notify the PM core of a wakeup event (signaled by @dev) that will take - * approximately @msec milliseconds to be processed by the kernel. Increment - * the counter of registered wakeup events and (if @msec is nonzero) set up - * the wakeup events timer to execute pm_wakeup_timer_fn() in future (if the - * timer has not been set up already, increment the counter of wakeup events - * being processed). If @dev is not NULL, the counter of wakeup events related - * to @dev is incremented too. - * - * It is safe to call this function from interrupt context. + * Call __pm_wakeup_event() for the @dev's wakeup source object. */ void pm_wakeup_event(struct device *dev, unsigned int msec) { unsigned long flags; - spin_lock_irqsave(&events_lock, flags); - event_count++; - if (dev) - dev->power.wakeup_count++; - - if (msec) { - unsigned long expires; + if (!dev) + return; - expires = jiffies + msecs_to_jiffies(msec); - if (!expires) - expires = 1; + spin_lock_irqsave(&dev->power.lock, flags); + __pm_wakeup_event(dev->power.wakeup, msec); + spin_unlock_irqrestore(&dev->power.lock, flags); +} +EXPORT_SYMBOL_GPL(pm_wakeup_event); - if (!events_timer_expires - || time_after(expires, events_timer_expires)) { - if (!events_timer_expires) - events_in_progress++; +/** + * pm_wakeup_update_hit_counts - Update hit counts of all active wakeup sources. + */ +static void pm_wakeup_update_hit_counts(void) +{ + unsigned long flags; + struct wakeup_source *ws; - mod_timer(&events_timer, expires); - events_timer_expires = expires; - } + rcu_read_lock(); + list_for_each_entry_rcu(ws, &wakeup_sources, entry) { + spin_lock_irqsave(&ws->lock, flags); + if (ws->active) + ws->hit_count++; + spin_unlock_irqrestore(&ws->lock, flags); } - spin_unlock_irqrestore(&events_lock, flags); + rcu_read_unlock(); } /** @@ -184,10 +554,13 @@ bool pm_check_wakeup_events(void) spin_lock_irqsave(&events_lock, flags); if (events_check_enabled) { - ret = (event_count == saved_event_count) && !events_in_progress; + ret = ((unsigned int)atomic_read(&event_count) == saved_count) + && !atomic_read(&events_in_progress); events_check_enabled = ret; } spin_unlock_irqrestore(&events_lock, flags); + if (!ret) + pm_wakeup_update_hit_counts(); return ret; } @@ -202,24 +575,20 @@ bool pm_check_wakeup_events(void) * drop down to zero has been interrupted by a signal (and the current number * of wakeup events being processed is still nonzero). Otherwise return true. */ -bool pm_get_wakeup_count(unsigned long *count) +bool pm_get_wakeup_count(unsigned int *count) { bool ret; - spin_lock_irq(&events_lock); if (capable(CAP_SYS_ADMIN)) events_check_enabled = false; - while (events_in_progress && !signal_pending(current)) { - spin_unlock_irq(&events_lock); - - schedule_timeout_interruptible(msecs_to_jiffies(100)); - - spin_lock_irq(&events_lock); + while (atomic_read(&events_in_progress) && !signal_pending(current)) { + pm_wakeup_update_hit_counts(); + schedule_timeout_interruptible(msecs_to_jiffies(TIMEOUT)); } - *count = event_count; - ret = !events_in_progress; - spin_unlock_irq(&events_lock); + + ret = !atomic_read(&events_in_progress); + *count = atomic_read(&event_count); return ret; } @@ -232,16 +601,19 @@ bool pm_get_wakeup_count(unsigned long *count) * old number of registered wakeup events to be used by pm_check_wakeup_events() * and return true. Otherwise return false. */ -bool pm_save_wakeup_count(unsigned long count) +bool pm_save_wakeup_count(unsigned int count) { bool ret = false; spin_lock_irq(&events_lock); - if (count == event_count && !events_in_progress) { - saved_event_count = count; + if (count == (unsigned int)atomic_read(&event_count) + && !atomic_read(&events_in_progress)) { + saved_count = count; events_check_enabled = true; ret = true; } spin_unlock_irq(&events_lock); + if (!ret) + pm_wakeup_update_hit_counts(); return ret; } diff --git a/include/linux/pm.h b/include/linux/pm.h index 52e8c55ff314..a84118911ced 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -448,23 +448,24 @@ enum rpm_request { RPM_REQ_RESUME, }; +struct wakeup_source; + struct dev_pm_info { pm_message_t power_state; unsigned int can_wakeup:1; - unsigned int should_wakeup:1; unsigned async_suspend:1; enum dpm_state status; /* Owned by the PM core */ + spinlock_t lock; #ifdef CONFIG_PM_SLEEP struct list_head entry; struct completion completion; - unsigned long wakeup_count; + struct wakeup_source *wakeup; #endif #ifdef CONFIG_PM_RUNTIME struct timer_list suspend_timer; unsigned long timer_expires; struct work_struct work; wait_queue_head_t wait_queue; - spinlock_t lock; atomic_t usage_count; atomic_t child_count; unsigned int disable_depth:3; @@ -559,11 +560,6 @@ extern void __suspend_report_result(const char *function, void *fn, int ret); } while (0) extern void device_pm_wait_for_dev(struct device *sub, struct device *dev); - -/* drivers/base/power/wakeup.c */ -extern void pm_wakeup_event(struct device *dev, unsigned int msec); -extern void pm_stay_awake(struct device *dev); -extern void pm_relax(void); #else /* !CONFIG_PM_SLEEP */ #define device_pm_lock() do {} while (0) @@ -577,10 +573,6 @@ static inline int dpm_suspend_start(pm_message_t state) #define suspend_report_result(fn, ret) do {} while (0) static inline void device_pm_wait_for_dev(struct device *a, struct device *b) {} - -static inline void pm_wakeup_event(struct device *dev, unsigned int msec) {} -static inline void pm_stay_awake(struct device *dev) {} -static inline void pm_relax(void) {} #endif /* !CONFIG_PM_SLEEP */ /* How to reorder dpm_list after device_move() */ diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index 76aca48722ae..9cff00dd6b63 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h @@ -2,6 +2,7 @@ * pm_wakeup.h - Power management wakeup interface * * Copyright (C) 2008 Alan Stern + * Copyright (C) 2010 Rafael J. Wysocki, Novell Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,19 +28,77 @@ #include -#ifdef CONFIG_PM - -/* Changes to device_may_wakeup take effect on the next pm state change. +/** + * struct wakeup_source - Representation of wakeup sources * - * By default, most devices should leave wakeup disabled. The exceptions - * are devices that everyone expects to be wakeup sources: keyboards, - * power buttons, possibly network interfaces, etc. + * @total_time: Total time this wakeup source has been active. + * @max_time: Maximum time this wakeup source has been continuously active. + * @last_time: Monotonic clock when the wakeup source's was activated last time. + * @event_count: Number of signaled wakeup events. + * @active_count: Number of times the wakeup sorce was activated. + * @relax_count: Number of times the wakeup sorce was deactivated. + * @hit_count: Number of times the wakeup sorce might abort system suspend. + * @active: Status of the wakeup source. */ -static inline void device_init_wakeup(struct device *dev, bool val) +struct wakeup_source { + char *name; + struct list_head entry; + spinlock_t lock; + struct timer_list timer; + unsigned long timer_expires; + ktime_t total_time; + ktime_t max_time; + ktime_t last_time; + unsigned long event_count; + unsigned long active_count; + unsigned long relax_count; + unsigned long hit_count; + unsigned int active:1; +}; + +#ifdef CONFIG_PM_SLEEP + +/* + * Changes to device_may_wakeup take effect on the next pm state change. + */ + +static inline void device_set_wakeup_capable(struct device *dev, bool capable) +{ + dev->power.can_wakeup = capable; +} + +static inline bool device_can_wakeup(struct device *dev) +{ + return dev->power.can_wakeup; +} + + + +static inline bool device_may_wakeup(struct device *dev) { - dev->power.can_wakeup = dev->power.should_wakeup = val; + return dev->power.can_wakeup && !!dev->power.wakeup; } +/* drivers/base/power/wakeup.c */ +extern struct wakeup_source *wakeup_source_create(const char *name); +extern void wakeup_source_destroy(struct wakeup_source *ws); +extern void wakeup_source_add(struct wakeup_source *ws); +extern void wakeup_source_remove(struct wakeup_source *ws); +extern struct wakeup_source *wakeup_source_register(const char *name); +extern void wakeup_source_unregister(struct wakeup_source *ws); +extern int device_wakeup_enable(struct device *dev); +extern int device_wakeup_disable(struct device *dev); +extern int device_init_wakeup(struct device *dev, bool val); +extern int device_set_wakeup_enable(struct device *dev, bool enable); +extern void __pm_stay_awake(struct wakeup_source *ws); +extern void pm_stay_awake(struct device *dev); +extern void __pm_relax(struct wakeup_source *ws); +extern void pm_relax(struct device *dev); +extern void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec); +extern void pm_wakeup_event(struct device *dev, unsigned int msec); + +#else /* !CONFIG_PM_SLEEP */ + static inline void device_set_wakeup_capable(struct device *dev, bool capable) { dev->power.can_wakeup = capable; @@ -50,43 +109,63 @@ static inline bool device_can_wakeup(struct device *dev) return dev->power.can_wakeup; } -static inline void device_set_wakeup_enable(struct device *dev, bool enable) +static inline bool device_may_wakeup(struct device *dev) { - dev->power.should_wakeup = enable; + return false; } -static inline bool device_may_wakeup(struct device *dev) +static inline struct wakeup_source *wakeup_source_create(const char *name) { - return dev->power.can_wakeup && dev->power.should_wakeup; + return NULL; } -#else /* !CONFIG_PM */ +static inline void wakeup_source_destroy(struct wakeup_source *ws) {} + +static inline void wakeup_source_add(struct wakeup_source *ws) {} -/* For some reason the following routines work even without CONFIG_PM */ -static inline void device_init_wakeup(struct device *dev, bool val) +static inline void wakeup_source_remove(struct wakeup_source *ws) {} + +static inline struct wakeup_source *wakeup_source_register(const char *name) { - dev->power.can_wakeup = val; + return NULL; } -static inline void device_set_wakeup_capable(struct device *dev, bool capable) +static inline void wakeup_source_unregister(struct wakeup_source *ws) {} + +static inline int device_wakeup_enable(struct device *dev) { - dev->power.can_wakeup = capable; + return -EINVAL; } -static inline bool device_can_wakeup(struct device *dev) +static inline int device_wakeup_disable(struct device *dev) { - return dev->power.can_wakeup; + return 0; } -static inline void device_set_wakeup_enable(struct device *dev, bool enable) +static inline int device_init_wakeup(struct device *dev, bool val) { + dev->power.can_wakeup = val; + return val ? -EINVAL : 0; } -static inline bool device_may_wakeup(struct device *dev) + +static inline int device_set_wakeup_enable(struct device *dev, bool enable) { - return false; + return -EINVAL; } -#endif /* !CONFIG_PM */ +static inline void __pm_stay_awake(struct wakeup_source *ws) {} + +static inline void pm_stay_awake(struct device *dev) {} + +static inline void __pm_relax(struct wakeup_source *ws) {} + +static inline void pm_relax(struct device *dev) {} + +static inline void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec) {} + +static inline void pm_wakeup_event(struct device *dev, unsigned int msec) {} + +#endif /* !CONFIG_PM_SLEEP */ #endif /* _LINUX_PM_WAKEUP_H */ diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 4af270ec2204..6b1712c51102 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -293,8 +293,8 @@ extern int unregister_pm_notifier(struct notifier_block *nb); extern bool events_check_enabled; extern bool pm_check_wakeup_events(void); -extern bool pm_get_wakeup_count(unsigned long *count); -extern bool pm_save_wakeup_count(unsigned long count); +extern bool pm_get_wakeup_count(unsigned int *count); +extern bool pm_save_wakeup_count(unsigned int count); #else /* !CONFIG_PM_SLEEP */ static inline int register_pm_notifier(struct notifier_block *nb) diff --git a/kernel/power/main.c b/kernel/power/main.c index f06ad6eff37a..6b12a0cf4d9f 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -237,18 +237,18 @@ static ssize_t wakeup_count_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - unsigned long val; + unsigned int val; - return pm_get_wakeup_count(&val) ? sprintf(buf, "%lu\n", val) : -EINTR; + return pm_get_wakeup_count(&val) ? sprintf(buf, "%u\n", val) : -EINTR; } static ssize_t wakeup_count_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) { - unsigned long val; + unsigned int val; - if (sscanf(buf, "%lu", &val) == 1) { + if (sscanf(buf, "%u", &val) == 1) { if (pm_save_wakeup_count(val)) return n; } -- cgit v1.2.3-70-g09d2 From 7490e44239e60293bca0c2663229050c36c660c2 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 25 Sep 2010 23:35:15 +0200 Subject: PM / Runtime: Add no_callbacks flag Some devices, such as USB interfaces, cannot be power-managed independently of their parents, i.e., they cannot be put in low power while the parent remains at full power. This patch (as1425) creates a new "no_callbacks" flag, which tells the PM core not to invoke the runtime-PM callback routines for the such devices but instead to assume that the callbacks always succeed. In addition, the non-debugging runtime-PM sysfs attributes for the devices are removed, since they are pretty much meaningless. The advantage of this scheme comes not so much from avoiding the callbacks themselves, but rather from the fact that without the need for a process context in which to run the callbacks, more work can be done in interrupt context. Signed-off-by: Alan Stern Signed-off-by: Rafael J. Wysocki --- Documentation/power/runtime_pm.txt | 37 +++++++++++++++++++++++++ drivers/base/power/power.h | 1 + drivers/base/power/runtime.c | 54 +++++++++++++++++++++++++++++++++++- drivers/base/power/sysfs.c | 56 +++++++++++++++++++++++++++++++++----- include/linux/pm.h | 7 +++++ include/linux/pm_runtime.h | 2 ++ 6 files changed, 149 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt index 55b859b3bc72..9ba49b21ac86 100644 --- a/Documentation/power/runtime_pm.txt +++ b/Documentation/power/runtime_pm.txt @@ -1,6 +1,7 @@ Run-time Power Management Framework for I/O Devices (C) 2009 Rafael J. Wysocki , Novell Inc. +(C) 2010 Alan Stern 1. Introduction @@ -230,6 +231,11 @@ defined in include/linux/pm.h: interface; it may only be modified with the help of the pm_runtime_allow() and pm_runtime_forbid() helper functions + unsigned int no_callbacks; + - indicates that the device does not use the run-time PM callbacks (see + Section 8); it may be modified only by the pm_runtime_no_callbacks() + helper function + All of the above fields are members of the 'power' member of 'struct device'. 4. Run-time PM Device Helper Functions @@ -349,6 +355,11 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: counter (used by the /sys/devices/.../power/control interface to effectively prevent the device from being power managed at run time) + void pm_runtime_no_callbacks(struct device *dev); + - set the power.no_callbacks flag for the device and remove the run-time + PM attributes from /sys/devices/.../power (or prevent them from being + added when the device is registered) + It is safe to execute the following helper functions from interrupt context: pm_request_idle() @@ -524,3 +535,29 @@ poweroff and run-time suspend callback, and similarly for system resume, thaw, restore, and run-time resume, can achieve this with the help of the UNIVERSAL_DEV_PM_OPS macro defined in include/linux/pm.h (possibly setting its last argument to NULL). + +8. "No-Callback" Devices + +Some "devices" are only logical sub-devices of their parent and cannot be +power-managed on their own. (The prototype example is a USB interface. Entire +USB devices can go into low-power mode or send wake-up requests, but neither is +possible for individual interfaces.) The drivers for these devices have no +need of run-time PM callbacks; if the callbacks did exist, ->runtime_suspend() +and ->runtime_resume() would always return 0 without doing anything else and +->runtime_idle() would always call pm_runtime_suspend(). + +Subsystems can tell the PM core about these devices by calling +pm_runtime_no_callbacks(). This should be done after the device structure is +initialized and before it is registered (although after device registration is +also okay). The routine will set the device's power.no_callbacks flag and +prevent the non-debugging run-time PM sysfs attributes from being created. + +When power.no_callbacks is set, the PM core will not invoke the +->runtime_idle(), ->runtime_suspend(), or ->runtime_resume() callbacks. +Instead it will assume that suspends and resumes always succeed and that idle +devices should be suspended. + +As a consequence, the PM core will never directly inform the device's subsystem +or driver about run-time power changes. Instead, the driver for the device's +parent must take responsibility for telling the device's driver when the +parent's power state changes. diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 8b2745c69e2a..698dde742587 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -60,6 +60,7 @@ static inline void device_pm_move_last(struct device *dev) {} extern int dpm_sysfs_add(struct device *); extern void dpm_sysfs_remove(struct device *); +extern void rpm_sysfs_remove(struct device *); #else /* CONFIG_PM */ diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index ed227b7c1bb5..5bd4daa93ef1 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -10,8 +10,10 @@ #include #include #include +#include "power.h" static int rpm_resume(struct device *dev, int rpmflags); +static int rpm_suspend(struct device *dev, int rpmflags); /** * update_pm_runtime_accounting - Update the time accounting of power states @@ -148,6 +150,12 @@ static int rpm_idle(struct device *dev, int rpmflags) /* Pending requests need to be canceled. */ dev->power.request = RPM_REQ_NONE; + if (dev->power.no_callbacks) { + /* Assume ->runtime_idle() callback would have suspended. */ + retval = rpm_suspend(dev, rpmflags); + goto out; + } + /* Carry out an asynchronous or a synchronous idle notification. */ if (rpmflags & RPM_ASYNC) { dev->power.request = RPM_REQ_IDLE; @@ -254,6 +262,10 @@ static int rpm_suspend(struct device *dev, int rpmflags) goto repeat; } + dev->power.deferred_resume = false; + if (dev->power.no_callbacks) + goto no_callback; /* Assume success. */ + /* Carry out an asynchronous or a synchronous suspend. */ if (rpmflags & RPM_ASYNC) { dev->power.request = RPM_REQ_SUSPEND; @@ -265,7 +277,6 @@ static int rpm_suspend(struct device *dev, int rpmflags) } __update_runtime_status(dev, RPM_SUSPENDING); - dev->power.deferred_resume = false; if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { spin_unlock_irq(&dev->power.lock); @@ -305,6 +316,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) pm_runtime_cancel_pending(dev); } } else { + no_callback: __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_deactivate_timer(dev); @@ -409,6 +421,23 @@ static int rpm_resume(struct device *dev, int rpmflags) goto repeat; } + /* + * See if we can skip waking up the parent. This is safe only if + * power.no_callbacks is set, because otherwise we don't know whether + * the resume will actually succeed. + */ + if (dev->power.no_callbacks && !parent && dev->parent) { + spin_lock(&dev->parent->power.lock); + if (dev->parent->power.disable_depth > 0 + || dev->parent->power.ignore_children + || dev->parent->power.runtime_status == RPM_ACTIVE) { + atomic_inc(&dev->parent->power.child_count); + spin_unlock(&dev->parent->power.lock); + goto no_callback; /* Assume success. */ + } + spin_unlock(&dev->parent->power.lock); + } + /* Carry out an asynchronous or a synchronous resume. */ if (rpmflags & RPM_ASYNC) { dev->power.request = RPM_REQ_RESUME; @@ -449,6 +478,9 @@ static int rpm_resume(struct device *dev, int rpmflags) goto repeat; } + if (dev->power.no_callbacks) + goto no_callback; /* Assume success. */ + __update_runtime_status(dev, RPM_RESUMING); if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { @@ -482,6 +514,7 @@ static int rpm_resume(struct device *dev, int rpmflags) __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_cancel_pending(dev); } else { + no_callback: __update_runtime_status(dev, RPM_ACTIVE); if (parent) atomic_inc(&parent->power.child_count); @@ -954,6 +987,25 @@ void pm_runtime_allow(struct device *dev) } EXPORT_SYMBOL_GPL(pm_runtime_allow); +/** + * pm_runtime_no_callbacks - Ignore run-time PM callbacks for a device. + * @dev: Device to handle. + * + * Set the power.no_callbacks flag, which tells the PM core that this + * device is power-managed through its parent and has no run-time PM + * callbacks of its own. The run-time sysfs attributes will be removed. + * + */ +void pm_runtime_no_callbacks(struct device *dev) +{ + spin_lock_irq(&dev->power.lock); + dev->power.no_callbacks = 1; + spin_unlock_irq(&dev->power.lock); + if (device_is_registered(dev)) + rpm_sysfs_remove(dev); +} +EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks); + /** * pm_runtime_init - Initialize run-time PM fields in given device object. * @dev: Device object to initialize. diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 8859780817e1..b5708c47ce2d 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -81,6 +81,9 @@ static const char enabled[] = "enabled"; static const char disabled[] = "disabled"; +const char power_group_name[] = "power"; +EXPORT_SYMBOL_GPL(power_group_name); + #ifdef CONFIG_PM_RUNTIME static const char ctrl_auto[] = "auto"; static const char ctrl_on[] = "on"; @@ -390,12 +393,6 @@ static DEVICE_ATTR(async, 0644, async_show, async_store); #endif /* CONFIG_PM_ADVANCED_DEBUG */ static struct attribute * power_attrs[] = { -#ifdef CONFIG_PM_RUNTIME - &dev_attr_control.attr, - &dev_attr_runtime_status.attr, - &dev_attr_runtime_suspended_time.attr, - &dev_attr_runtime_active_time.attr, -#endif &dev_attr_wakeup.attr, #ifdef CONFIG_PM_SLEEP &dev_attr_wakeup_count.attr, @@ -409,6 +406,7 @@ static struct attribute * power_attrs[] = { #ifdef CONFIG_PM_ADVANCED_DEBUG &dev_attr_async.attr, #ifdef CONFIG_PM_RUNTIME + &dev_attr_runtime_status.attr, &dev_attr_runtime_usage.attr, &dev_attr_runtime_active_kids.attr, &dev_attr_runtime_enabled.attr, @@ -417,10 +415,52 @@ static struct attribute * power_attrs[] = { NULL, }; static struct attribute_group pm_attr_group = { - .name = "power", + .name = power_group_name, .attrs = power_attrs, }; +#ifdef CONFIG_PM_RUNTIME + +static struct attribute *runtime_attrs[] = { +#ifndef CONFIG_PM_ADVANCED_DEBUG + &dev_attr_runtime_status.attr, +#endif + &dev_attr_control.attr, + &dev_attr_runtime_suspended_time.attr, + &dev_attr_runtime_active_time.attr, + NULL, +}; +static struct attribute_group pm_runtime_attr_group = { + .name = power_group_name, + .attrs = runtime_attrs, +}; + +int dpm_sysfs_add(struct device *dev) +{ + int rc; + + rc = sysfs_create_group(&dev->kobj, &pm_attr_group); + if (rc == 0 && !dev->power.no_callbacks) { + rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group); + if (rc) + sysfs_remove_group(&dev->kobj, &pm_attr_group); + } + return rc; +} + +void rpm_sysfs_remove(struct device *dev) +{ + sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group); +} + +void dpm_sysfs_remove(struct device *dev) +{ + rpm_sysfs_remove(dev); + sysfs_remove_group(&dev->kobj, &pm_attr_group); +} + +#else /* CONFIG_PM_RUNTIME */ + int dpm_sysfs_add(struct device * dev) { return sysfs_create_group(&dev->kobj, &pm_attr_group); @@ -430,3 +470,5 @@ void dpm_sysfs_remove(struct device * dev) { sysfs_remove_group(&dev->kobj, &pm_attr_group); } + +#endif diff --git a/include/linux/pm.h b/include/linux/pm.h index 1abfe84f447d..abd81ffaba3c 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -41,6 +41,12 @@ extern void (*pm_power_off_prepare)(void); struct device; +#ifdef CONFIG_PM +extern const char power_group_name[]; /* = "power" */ +#else +#define power_group_name NULL +#endif + typedef struct pm_message { int event; } pm_message_t; @@ -475,6 +481,7 @@ struct dev_pm_info { unsigned int deferred_resume:1; unsigned int run_wake:1; unsigned int runtime_auto:1; + unsigned int no_callbacks:1; enum rpm_request request; enum rpm_status runtime_status; int runtime_error; diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 5869d87fffac..8ca52f7c357e 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -36,6 +36,7 @@ extern void pm_runtime_forbid(struct device *dev); extern int pm_generic_runtime_idle(struct device *dev); extern int pm_generic_runtime_suspend(struct device *dev); extern int pm_generic_runtime_resume(struct device *dev); +extern void pm_runtime_no_callbacks(struct device *dev); static inline bool pm_children_suspended(struct device *dev) { @@ -110,6 +111,7 @@ static inline bool pm_runtime_suspended(struct device *dev) { return false; } static inline int pm_generic_runtime_idle(struct device *dev) { return 0; } static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } +static inline void pm_runtime_no_callbacks(struct device *dev) {} #endif /* !CONFIG_PM_RUNTIME */ -- cgit v1.2.3-70-g09d2 From 15bcb91d7e607d8a2e060f01f7784a7454668da4 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 25 Sep 2010 23:35:21 +0200 Subject: PM / Runtime: Implement autosuspend support This patch (as1427) implements the "autosuspend" facility for runtime PM. A few new fields are added to the dev_pm_info structure and several new PM helper functions are defined, for telling the PM core whether or not a device uses autosuspend, for setting the autosuspend delay, and for marking periods of device activity. Drivers that do not want to use autosuspend can continue using the same helper functions as before; their behavior will not change. In addition, drivers supporting autosuspend can also call the old helper functions to get the old behavior. The details are all explained in Documentation/power/runtime_pm.txt and Documentation/ABI/testing/sysfs-devices-power. Signed-off-by: Alan Stern Signed-off-by: Rafael J. Wysocki --- Documentation/ABI/testing/sysfs-devices-power | 18 +++ Documentation/power/runtime_pm.txt | 190 +++++++++++++++++++++++++- drivers/base/power/runtime.c | 186 ++++++++++++++++++++++++- drivers/base/power/sysfs.c | 40 ++++++ include/linux/pm.h | 8 ++ include/linux/pm_runtime.h | 45 ++++++ 6 files changed, 473 insertions(+), 14 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index 6bb2dd3c3a71..7628cd1bc36a 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power @@ -147,3 +147,21 @@ Description: milliseconds. This attribute is read-only. If the device is not enabled to wake up the system from sleep states, this attribute is empty. + +What: /sys/devices/.../power/autosuspend_delay_ms +Date: September 2010 +Contact: Alan Stern +Description: + The /sys/devices/.../power/autosuspend_delay_ms attribute + contains the autosuspend delay value (in milliseconds). Some + drivers do not want their device to suspend as soon as it + becomes idle at run time; they want the device to remain + inactive for a certain minimum period of time first. That + period is called the autosuspend delay. Negative values will + prevent the device from being suspended at run time (similar + to writing "on" to the power/control attribute). Values >= + 1000 will cause the autosuspend timer expiration to be rounded + up to the nearest second. + + Not all drivers support this attribute. If it isn't supported, + attempts to read or write it will yield I/O errors. diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt index 9ba49b21ac86..489e9bacd165 100644 --- a/Documentation/power/runtime_pm.txt +++ b/Documentation/power/runtime_pm.txt @@ -158,7 +158,8 @@ rules: to execute it, the other callbacks will not be executed for the same device. * A request to execute ->runtime_resume() will cancel any pending or - scheduled requests to execute the other callbacks for the same device. + scheduled requests to execute the other callbacks for the same device, + except for scheduled autosuspends. 3. Run-time PM Device Fields @@ -166,7 +167,7 @@ The following device run-time PM fields are present in 'struct dev_pm_info', as defined in include/linux/pm.h: struct timer_list suspend_timer; - - timer used for scheduling (delayed) suspend request + - timer used for scheduling (delayed) suspend and autosuspend requests unsigned long timer_expires; - timer expiration time, in jiffies (if this is different from zero, the @@ -236,6 +237,23 @@ defined in include/linux/pm.h: Section 8); it may be modified only by the pm_runtime_no_callbacks() helper function + unsigned int use_autosuspend; + - indicates that the device's driver supports delayed autosuspend (see + Section 9); it may be modified only by the + pm_runtime{_dont}_use_autosuspend() helper functions + + unsigned int timer_autosuspends; + - indicates that the PM core should attempt to carry out an autosuspend + when the timer expires rather than a normal suspend + + int autosuspend_delay; + - the delay time (in milliseconds) to be used for autosuspend + + unsigned long last_busy; + - the time (in jiffies) when the pm_runtime_mark_last_busy() helper + function was last called for this device; used in calculating inactivity + periods for autosuspend + All of the above fields are members of the 'power' member of 'struct device'. 4. Run-time PM Device Helper Functions @@ -261,6 +279,12 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: error code on failure, where -EAGAIN or -EBUSY means it is safe to attempt to suspend the device again in future + int pm_runtime_autosuspend(struct device *dev); + - same as pm_runtime_suspend() except that the autosuspend delay is taken + into account; if pm_runtime_autosuspend_expiration() says the delay has + not yet expired then an autosuspend is scheduled for the appropriate time + and 0 is returned + int pm_runtime_resume(struct device *dev); - execute the subsystem-level resume callback for the device; returns 0 on success, 1 if the device's run-time PM status was already 'active' or @@ -273,6 +297,11 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: device (the request is represented by a work item in pm_wq); returns 0 on success or error code if the request has not been queued up + int pm_request_autosuspend(struct device *dev); + - schedule the execution of the subsystem-level suspend callback for the + device when the autosuspend delay has expired; if the delay has already + expired then the work item is queued up immediately + int pm_schedule_suspend(struct device *dev, unsigned int delay); - schedule the execution of the subsystem-level suspend callback for the device in future, where 'delay' is the time to wait before queuing up a @@ -304,12 +333,20 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: - decrement the device's usage counter int pm_runtime_put(struct device *dev); - - decrement the device's usage counter, run pm_request_idle(dev) and return - its result + - decrement the device's usage counter; if the result is 0 then run + pm_request_idle(dev) and return its result + + int pm_runtime_put_autosuspend(struct device *dev); + - decrement the device's usage counter; if the result is 0 then run + pm_request_autosuspend(dev) and return its result int pm_runtime_put_sync(struct device *dev); - - decrement the device's usage counter, run pm_runtime_idle(dev) and return - its result + - decrement the device's usage counter; if the result is 0 then run + pm_runtime_idle(dev) and return its result + + int pm_runtime_put_sync_autosuspend(struct device *dev); + - decrement the device's usage counter; if the result is 0 then run + pm_runtime_autosuspend(dev) and return its result void pm_runtime_enable(struct device *dev); - enable the run-time PM helper functions to run the device bus type's @@ -360,19 +397,46 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: PM attributes from /sys/devices/.../power (or prevent them from being added when the device is registered) + void pm_runtime_mark_last_busy(struct device *dev); + - set the power.last_busy field to the current time + + void pm_runtime_use_autosuspend(struct device *dev); + - set the power.use_autosuspend flag, enabling autosuspend delays + + void pm_runtime_dont_use_autosuspend(struct device *dev); + - clear the power.use_autosuspend flag, disabling autosuspend delays + + void pm_runtime_set_autosuspend_delay(struct device *dev, int delay); + - set the power.autosuspend_delay value to 'delay' (expressed in + milliseconds); if 'delay' is negative then run-time suspends are + prevented + + unsigned long pm_runtime_autosuspend_expiration(struct device *dev); + - calculate the time when the current autosuspend delay period will expire, + based on power.last_busy and power.autosuspend_delay; if the delay time + is 1000 ms or larger then the expiration time is rounded up to the + nearest second; returns 0 if the delay period has already expired or + power.use_autosuspend isn't set, otherwise returns the expiration time + in jiffies + It is safe to execute the following helper functions from interrupt context: pm_request_idle() +pm_request_autosuspend() pm_schedule_suspend() pm_request_resume() pm_runtime_get_noresume() pm_runtime_get() pm_runtime_put_noidle() pm_runtime_put() +pm_runtime_put_autosuspend() +pm_runtime_enable() pm_suspend_ignore_children() pm_runtime_set_active() pm_runtime_set_suspended() -pm_runtime_enable() +pm_runtime_suspended() +pm_runtime_mark_last_busy() +pm_runtime_autosuspend_expiration() 5. Run-time PM Initialization, Device Probing and Removal @@ -561,3 +625,115 @@ As a consequence, the PM core will never directly inform the device's subsystem or driver about run-time power changes. Instead, the driver for the device's parent must take responsibility for telling the device's driver when the parent's power state changes. + +9. Autosuspend, or automatically-delayed suspends + +Changing a device's power state isn't free; it requires both time and energy. +A device should be put in a low-power state only when there's some reason to +think it will remain in that state for a substantial time. A common heuristic +says that a device which hasn't been used for a while is liable to remain +unused; following this advice, drivers should not allow devices to be suspended +at run-time until they have been inactive for some minimum period. Even when +the heuristic ends up being non-optimal, it will still prevent devices from +"bouncing" too rapidly between low-power and full-power states. + +The term "autosuspend" is an historical remnant. It doesn't mean that the +device is automatically suspended (the subsystem or driver still has to call +the appropriate PM routines); rather it means that run-time suspends will +automatically be delayed until the desired period of inactivity has elapsed. + +Inactivity is determined based on the power.last_busy field. Drivers should +call pm_runtime_mark_last_busy() to update this field after carrying out I/O, +typically just before calling pm_runtime_put_autosuspend(). The desired length +of the inactivity period is a matter of policy. Subsystems can set this length +initially by calling pm_runtime_set_autosuspend_delay(), but after device +registration the length should be controlled by user space, using the +/sys/devices/.../power/autosuspend_delay_ms attribute. + +In order to use autosuspend, subsystems or drivers must call +pm_runtime_use_autosuspend() (preferably before registering the device), and +thereafter they should use the various *_autosuspend() helper functions instead +of the non-autosuspend counterparts: + + Instead of: pm_runtime_suspend use: pm_runtime_autosuspend; + Instead of: pm_schedule_suspend use: pm_request_autosuspend; + Instead of: pm_runtime_put use: pm_runtime_put_autosuspend; + Instead of: pm_runtime_put_sync use: pm_runtime_put_sync_autosuspend. + +Drivers may also continue to use the non-autosuspend helper functions; they +will behave normally, not taking the autosuspend delay into account. +Similarly, if the power.use_autosuspend field isn't set then the autosuspend +helper functions will behave just like the non-autosuspend counterparts. + +The implementation is well suited for asynchronous use in interrupt contexts. +However such use inevitably involves races, because the PM core can't +synchronize ->runtime_suspend() callbacks with the arrival of I/O requests. +This synchronization must be handled by the driver, using its private lock. +Here is a schematic pseudo-code example: + + foo_read_or_write(struct foo_priv *foo, void *data) + { + lock(&foo->private_lock); + add_request_to_io_queue(foo, data); + if (foo->num_pending_requests++ == 0) + pm_runtime_get(&foo->dev); + if (!foo->is_suspended) + foo_process_next_request(foo); + unlock(&foo->private_lock); + } + + foo_io_completion(struct foo_priv *foo, void *req) + { + lock(&foo->private_lock); + if (--foo->num_pending_requests == 0) { + pm_runtime_mark_last_busy(&foo->dev); + pm_runtime_put_autosuspend(&foo->dev); + } else { + foo_process_next_request(foo); + } + unlock(&foo->private_lock); + /* Send req result back to the user ... */ + } + + int foo_runtime_suspend(struct device *dev) + { + struct foo_priv foo = container_of(dev, ...); + int ret = 0; + + lock(&foo->private_lock); + if (foo->num_pending_requests > 0) { + ret = -EBUSY; + } else { + /* ... suspend the device ... */ + foo->is_suspended = 1; + } + unlock(&foo->private_lock); + return ret; + } + + int foo_runtime_resume(struct device *dev) + { + struct foo_priv foo = container_of(dev, ...); + + lock(&foo->private_lock); + /* ... resume the device ... */ + foo->is_suspended = 0; + pm_runtime_mark_last_busy(&foo->dev); + if (foo->num_pending_requests > 0) + foo_process_requests(foo); + unlock(&foo->private_lock); + return 0; + } + +The important point is that after foo_io_completion() asks for an autosuspend, +the foo_runtime_suspend() callback may race with foo_read_or_write(). +Therefore foo_runtime_suspend() has to check whether there are any pending I/O +requests (while holding the private lock) before allowing the suspend to +proceed. + +In addition, the power.autosuspend_delay field can be changed by user space at +any time. If a driver cares about this, it can call +pm_runtime_autosuspend_expiration() from within the ->runtime_suspend() +callback while holding its private lock. If the function returns a nonzero +value then the delay has not yet expired and the callback should return +-EAGAIN. diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 5bd4daa93ef1..cd4e100a1362 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -9,7 +9,6 @@ #include #include -#include #include "power.h" static int rpm_resume(struct device *dev, int rpmflags); @@ -79,6 +78,53 @@ static void pm_runtime_cancel_pending(struct device *dev) dev->power.request = RPM_REQ_NONE; } +/* + * pm_runtime_autosuspend_expiration - Get a device's autosuspend-delay expiration time. + * @dev: Device to handle. + * + * Compute the autosuspend-delay expiration time based on the device's + * power.last_busy time. If the delay has already expired or is disabled + * (negative) or the power.use_autosuspend flag isn't set, return 0. + * Otherwise return the expiration time in jiffies (adjusted to be nonzero). + * + * This function may be called either with or without dev->power.lock held. + * Either way it can be racy, since power.last_busy may be updated at any time. + */ +unsigned long pm_runtime_autosuspend_expiration(struct device *dev) +{ + int autosuspend_delay; + long elapsed; + unsigned long last_busy; + unsigned long expires = 0; + + if (!dev->power.use_autosuspend) + goto out; + + autosuspend_delay = ACCESS_ONCE(dev->power.autosuspend_delay); + if (autosuspend_delay < 0) + goto out; + + last_busy = ACCESS_ONCE(dev->power.last_busy); + elapsed = jiffies - last_busy; + if (elapsed < 0) + goto out; /* jiffies has wrapped around. */ + + /* + * If the autosuspend_delay is >= 1 second, align the timer by rounding + * up to the nearest second. + */ + expires = last_busy + msecs_to_jiffies(autosuspend_delay); + if (autosuspend_delay >= 1000) + expires = round_jiffies(expires); + expires += !expires; + if (elapsed >= expires - last_busy) + expires = 0; /* Already expired. */ + + out: + return expires; +} +EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration); + /** * rpm_check_suspend_allowed - Test whether a device may be suspended. * @dev: Device to test. @@ -234,6 +280,32 @@ static int rpm_suspend(struct device *dev, int rpmflags) if (retval) goto out; + /* If the autosuspend_delay time hasn't expired yet, reschedule. */ + if ((rpmflags & RPM_AUTO) + && dev->power.runtime_status != RPM_SUSPENDING) { + unsigned long expires = pm_runtime_autosuspend_expiration(dev); + + if (expires != 0) { + /* Pending requests need to be canceled. */ + dev->power.request = RPM_REQ_NONE; + + /* + * Optimization: If the timer is already running and is + * set to expire at or before the autosuspend delay, + * avoid the overhead of resetting it. Just let it + * expire; pm_suspend_timer_fn() will take care of the + * rest. + */ + if (!(dev->power.timer_expires && time_before_eq( + dev->power.timer_expires, expires))) { + dev->power.timer_expires = expires; + mod_timer(&dev->power.suspend_timer, expires); + } + dev->power.timer_autosuspends = 1; + goto out; + } + } + /* Other scheduled or pending requests need to be canceled. */ pm_runtime_cancel_pending(dev); @@ -268,7 +340,8 @@ static int rpm_suspend(struct device *dev, int rpmflags) /* Carry out an asynchronous or a synchronous suspend. */ if (rpmflags & RPM_ASYNC) { - dev->power.request = RPM_REQ_SUSPEND; + dev->power.request = (rpmflags & RPM_AUTO) ? + RPM_REQ_AUTOSUSPEND : RPM_REQ_SUSPEND; if (!dev->power.request_pending) { dev->power.request_pending = true; queue_work(pm_wq, &dev->power.work); @@ -383,8 +456,15 @@ static int rpm_resume(struct device *dev, int rpmflags) if (retval) goto out; - /* Other scheduled or pending requests need to be canceled. */ - pm_runtime_cancel_pending(dev); + /* + * Other scheduled or pending requests need to be canceled. Small + * optimization: If an autosuspend timer is running, leave it running + * rather than cancelling it now only to restart it again in the near + * future. + */ + dev->power.request = RPM_REQ_NONE; + if (!dev->power.timer_autosuspends) + pm_runtime_deactivate_timer(dev); if (dev->power.runtime_status == RPM_ACTIVE) { retval = 1; @@ -568,6 +648,9 @@ static void pm_runtime_work(struct work_struct *work) case RPM_REQ_SUSPEND: rpm_suspend(dev, RPM_NOWAIT); break; + case RPM_REQ_AUTOSUSPEND: + rpm_suspend(dev, RPM_NOWAIT | RPM_AUTO); + break; case RPM_REQ_RESUME: rpm_resume(dev, RPM_NOWAIT); break; @@ -595,7 +678,8 @@ static void pm_suspend_timer_fn(unsigned long data) /* If 'expire' is after 'jiffies' we've been called too early. */ if (expires > 0 && !time_after(expires, jiffies)) { dev->power.timer_expires = 0; - rpm_suspend(dev, RPM_ASYNC); + rpm_suspend(dev, dev->power.timer_autosuspends ? + (RPM_ASYNC | RPM_AUTO) : RPM_ASYNC); } spin_unlock_irqrestore(&dev->power.lock, flags); @@ -627,6 +711,7 @@ int pm_schedule_suspend(struct device *dev, unsigned int delay) dev->power.timer_expires = jiffies + msecs_to_jiffies(delay); dev->power.timer_expires += !dev->power.timer_expires; + dev->power.timer_autosuspends = 0; mod_timer(&dev->power.suspend_timer, dev->power.timer_expires); out: @@ -670,7 +755,9 @@ EXPORT_SYMBOL_GPL(__pm_runtime_idle); * @dev: Device to suspend. * @rpmflags: Flag bits. * - * Carry out a suspend, either synchronous or asynchronous. + * If the RPM_GET_PUT flag is set, decrement the device's usage count and + * return immediately if it is larger than zero. Then carry out a suspend, + * either synchronous or asynchronous. * * This routine may be called in atomic context if the RPM_ASYNC flag is set. */ @@ -679,6 +766,11 @@ int __pm_runtime_suspend(struct device *dev, int rpmflags) unsigned long flags; int retval; + if (rpmflags & RPM_GET_PUT) { + if (!atomic_dec_and_test(&dev->power.usage_count)) + return 0; + } + spin_lock_irqsave(&dev->power.lock, flags); retval = rpm_suspend(dev, rpmflags); spin_unlock_irqrestore(&dev->power.lock, flags); @@ -980,7 +1072,7 @@ void pm_runtime_allow(struct device *dev) dev->power.runtime_auto = true; if (atomic_dec_and_test(&dev->power.usage_count)) - rpm_idle(dev, 0); + rpm_idle(dev, RPM_AUTO); out: spin_unlock_irq(&dev->power.lock); @@ -1006,6 +1098,86 @@ void pm_runtime_no_callbacks(struct device *dev) } EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks); +/** + * update_autosuspend - Handle a change to a device's autosuspend settings. + * @dev: Device to handle. + * @old_delay: The former autosuspend_delay value. + * @old_use: The former use_autosuspend value. + * + * Prevent runtime suspend if the new delay is negative and use_autosuspend is + * set; otherwise allow it. Send an idle notification if suspends are allowed. + * + * This function must be called under dev->power.lock with interrupts disabled. + */ +static void update_autosuspend(struct device *dev, int old_delay, int old_use) +{ + int delay = dev->power.autosuspend_delay; + + /* Should runtime suspend be prevented now? */ + if (dev->power.use_autosuspend && delay < 0) { + + /* If it used to be allowed then prevent it. */ + if (!old_use || old_delay >= 0) { + atomic_inc(&dev->power.usage_count); + rpm_resume(dev, 0); + } + } + + /* Runtime suspend should be allowed now. */ + else { + + /* If it used to be prevented then allow it. */ + if (old_use && old_delay < 0) + atomic_dec(&dev->power.usage_count); + + /* Maybe we can autosuspend now. */ + rpm_idle(dev, RPM_AUTO); + } +} + +/** + * pm_runtime_set_autosuspend_delay - Set a device's autosuspend_delay value. + * @dev: Device to handle. + * @delay: Value of the new delay in milliseconds. + * + * Set the device's power.autosuspend_delay value. If it changes to negative + * and the power.use_autosuspend flag is set, prevent run-time suspends. If it + * changes the other way, allow run-time suspends. + */ +void pm_runtime_set_autosuspend_delay(struct device *dev, int delay) +{ + int old_delay, old_use; + + spin_lock_irq(&dev->power.lock); + old_delay = dev->power.autosuspend_delay; + old_use = dev->power.use_autosuspend; + dev->power.autosuspend_delay = delay; + update_autosuspend(dev, old_delay, old_use); + spin_unlock_irq(&dev->power.lock); +} +EXPORT_SYMBOL_GPL(pm_runtime_set_autosuspend_delay); + +/** + * __pm_runtime_use_autosuspend - Set a device's use_autosuspend flag. + * @dev: Device to handle. + * @use: New value for use_autosuspend. + * + * Set the device's power.use_autosuspend flag, and allow or prevent run-time + * suspends as needed. + */ +void __pm_runtime_use_autosuspend(struct device *dev, bool use) +{ + int old_delay, old_use; + + spin_lock_irq(&dev->power.lock); + old_delay = dev->power.autosuspend_delay; + old_use = dev->power.use_autosuspend; + dev->power.use_autosuspend = use; + update_autosuspend(dev, old_delay, old_use); + spin_unlock_irq(&dev->power.lock); +} +EXPORT_SYMBOL_GPL(__pm_runtime_use_autosuspend); + /** * pm_runtime_init - Initialize run-time PM fields in given device object. * @dev: Device object to initialize. diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index b5708c47ce2d..0b1e46bf3e56 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -75,6 +75,18 @@ * attribute is set to "enabled" by bus type code or device drivers and in * that cases it should be safe to leave the default value. * + * autosuspend_delay_ms - Report/change a device's autosuspend_delay value + * + * Some drivers don't want to carry out a runtime suspend as soon as a + * device becomes idle; they want it always to remain idle for some period + * of time before suspending it. This period is the autosuspend_delay + * value (expressed in milliseconds) and it can be controlled by the user. + * If the value is negative then the device will never be runtime + * suspended. + * + * NOTE: The autosuspend_delay_ms attribute and the autosuspend_delay + * value are used only if the driver calls pm_runtime_use_autosuspend(). + * * wakeup_count - Report the number of wakeup events related to the device */ @@ -173,6 +185,33 @@ static ssize_t rtpm_status_show(struct device *dev, } static DEVICE_ATTR(runtime_status, 0444, rtpm_status_show, NULL); + +static ssize_t autosuspend_delay_ms_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!dev->power.use_autosuspend) + return -EIO; + return sprintf(buf, "%d\n", dev->power.autosuspend_delay); +} + +static ssize_t autosuspend_delay_ms_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t n) +{ + long delay; + + if (!dev->power.use_autosuspend) + return -EIO; + + if (strict_strtol(buf, 10, &delay) != 0 || delay != (int) delay) + return -EINVAL; + + pm_runtime_set_autosuspend_delay(dev, delay); + return n; +} + +static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show, + autosuspend_delay_ms_store); + #endif static ssize_t @@ -428,6 +467,7 @@ static struct attribute *runtime_attrs[] = { &dev_attr_control.attr, &dev_attr_runtime_suspended_time.attr, &dev_attr_runtime_active_time.attr, + &dev_attr_autosuspend_delay_ms.attr, NULL, }; static struct attribute_group pm_runtime_attr_group = { diff --git a/include/linux/pm.h b/include/linux/pm.h index abd81ffaba3c..40f3f45702ba 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -444,6 +444,9 @@ enum rpm_status { * * RPM_REQ_SUSPEND Run the device bus type's ->runtime_suspend() callback * + * RPM_REQ_AUTOSUSPEND Same as RPM_REQ_SUSPEND, but not until the device has + * been inactive for as long as power.autosuspend_delay + * * RPM_REQ_RESUME Run the device bus type's ->runtime_resume() callback */ @@ -451,6 +454,7 @@ enum rpm_request { RPM_REQ_NONE = 0, RPM_REQ_IDLE, RPM_REQ_SUSPEND, + RPM_REQ_AUTOSUSPEND, RPM_REQ_RESUME, }; @@ -482,9 +486,13 @@ struct dev_pm_info { unsigned int run_wake:1; unsigned int runtime_auto:1; unsigned int no_callbacks:1; + unsigned int use_autosuspend:1; + unsigned int timer_autosuspends:1; enum rpm_request request; enum rpm_status runtime_status; int runtime_error; + int autosuspend_delay; + unsigned long last_busy; unsigned long active_jiffies; unsigned long suspended_jiffies; unsigned long accounting_timestamp; diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 8ca52f7c357e..99ed1aa8f933 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -12,12 +12,15 @@ #include #include +#include + /* Runtime PM flag argument bits */ #define RPM_ASYNC 0x01 /* Request is asynchronous */ #define RPM_NOWAIT 0x02 /* Don't wait for concurrent state change */ #define RPM_GET_PUT 0x04 /* Increment/decrement the usage_count */ +#define RPM_AUTO 0x08 /* Use autosuspend_delay */ #ifdef CONFIG_PM_RUNTIME @@ -37,6 +40,9 @@ extern int pm_generic_runtime_idle(struct device *dev); extern int pm_generic_runtime_suspend(struct device *dev); extern int pm_generic_runtime_resume(struct device *dev); extern void pm_runtime_no_callbacks(struct device *dev); +extern void __pm_runtime_use_autosuspend(struct device *dev, bool use); +extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay); +extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev); static inline bool pm_children_suspended(struct device *dev) { @@ -74,6 +80,11 @@ static inline bool pm_runtime_suspended(struct device *dev) return dev->power.runtime_status == RPM_SUSPENDED; } +static inline void pm_runtime_mark_last_busy(struct device *dev) +{ + ACCESS_ONCE(dev->power.last_busy) = jiffies; +} + #else /* !CONFIG_PM_RUNTIME */ static inline int __pm_runtime_idle(struct device *dev, int rpmflags) @@ -113,6 +124,14 @@ static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } static inline void pm_runtime_no_callbacks(struct device *dev) {} +static inline void pm_runtime_mark_last_busy(struct device *dev) {} +static inline void __pm_runtime_use_autosuspend(struct device *dev, + bool use) {} +static inline void pm_runtime_set_autosuspend_delay(struct device *dev, + int delay) {} +static inline unsigned long pm_runtime_autosuspend_expiration( + struct device *dev) { return 0; } + #endif /* !CONFIG_PM_RUNTIME */ static inline int pm_runtime_idle(struct device *dev) @@ -125,6 +144,11 @@ static inline int pm_runtime_suspend(struct device *dev) return __pm_runtime_suspend(dev, 0); } +static inline int pm_runtime_autosuspend(struct device *dev) +{ + return __pm_runtime_suspend(dev, RPM_AUTO); +} + static inline int pm_runtime_resume(struct device *dev) { return __pm_runtime_resume(dev, 0); @@ -155,11 +179,22 @@ static inline int pm_runtime_put(struct device *dev) return __pm_runtime_idle(dev, RPM_GET_PUT | RPM_ASYNC); } +static inline int pm_runtime_put_autosuspend(struct device *dev) +{ + return __pm_runtime_suspend(dev, + RPM_GET_PUT | RPM_ASYNC | RPM_AUTO); +} + static inline int pm_runtime_put_sync(struct device *dev) { return __pm_runtime_idle(dev, RPM_GET_PUT); } +static inline int pm_runtime_put_sync_autosuspend(struct device *dev) +{ + return __pm_runtime_suspend(dev, RPM_GET_PUT | RPM_AUTO); +} + static inline int pm_runtime_set_active(struct device *dev) { return __pm_runtime_set_status(dev, RPM_ACTIVE); @@ -175,4 +210,14 @@ static inline void pm_runtime_disable(struct device *dev) __pm_runtime_disable(dev, true); } +static inline void pm_runtime_use_autosuspend(struct device *dev) +{ + __pm_runtime_use_autosuspend(dev, true); +} + +static inline void pm_runtime_dont_use_autosuspend(struct device *dev) +{ + __pm_runtime_use_autosuspend(dev, false); +} + #endif -- cgit v1.2.3-70-g09d2 From d33ac60beaf2c7dee5cd90aba7c1eb385dd70937 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 12 Oct 2010 00:00:25 +0200 Subject: PM: Add sysfs attr for rechecking dev hash from PM trace If the device which fails to resume is part of a loadable kernel module it won't be checked at startup against the magic number stored in the RTC. Add a read-only sysfs attribute /sys/power/pm_trace_dev_match which contains a list of newline separated devices (usually just the one) which currently match the last magic number. This allows the device which is failing to resume to be found after the modules are loaded again. Signed-off-by: James Hogan Signed-off-by: Rafael J. Wysocki --- Documentation/ABI/testing/sysfs-power | 29 +++++++++++++++++++++++++++++ Documentation/power/s2ram.txt | 7 +++++++ drivers/base/power/trace.c | 31 +++++++++++++++++++++++++++++++ include/linux/resume-trace.h | 2 ++ kernel/power/main.c | 18 ++++++++++++++++++ 5 files changed, 87 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power index 2875f1f74a07..194ca446ac28 100644 --- a/Documentation/ABI/testing/sysfs-power +++ b/Documentation/ABI/testing/sysfs-power @@ -99,9 +99,38 @@ Description: dmesg -s 1000000 | grep 'hash matches' + If you do not get any matches (or they appear to be false + positives), it is possible that the last PM event point + referred to a device created by a loadable kernel module. In + this case cat /sys/power/pm_trace_dev_match (see below) after + your system is started up and the kernel modules are loaded. + CAUTION: Using it will cause your machine's real-time (CMOS) clock to be set to a random invalid time after a resume. +What; /sys/power/pm_trace_dev_match +Date: October 2010 +Contact: James Hogan +Description: + The /sys/power/pm_trace_dev_match file contains the name of the + device associated with the last PM event point saved in the RTC + across reboots when pm_trace has been used. More precisely it + contains the list of current devices (including those + registered by loadable kernel modules since boot) which match + the device hash in the RTC at boot, with a newline after each + one. + + The advantage of this file over the hash matches printed to the + kernel log (see /sys/power/pm_trace), is that it includes + devices created after boot by loadable kernel modules. + + Due to the small hash size necessary to fit in the RTC, it is + possible that more than one device matches the hash, in which + case further investigation is required to determine which + device is causing the problem. Note that genuine RTC clock + values (such as when pm_trace has not been used), can still + match a device and output it's name here. + What: /sys/power/pm_async Date: January 2009 Contact: Rafael J. Wysocki diff --git a/Documentation/power/s2ram.txt b/Documentation/power/s2ram.txt index 514b94fc931e..1bdfa0443773 100644 --- a/Documentation/power/s2ram.txt +++ b/Documentation/power/s2ram.txt @@ -49,6 +49,13 @@ machine that doesn't boot) is: device (lspci and /sys/devices/pci* is your friend), and see if you can fix it, disable it, or trace into its resume function. + If no device matches the hash (or any matches appear to be false positives), + the culprit may be a device from a loadable kernel module that is not loaded + until after the hash is checked. You can check the hash against the current + devices again after more modules are loaded using sysfs: + + cat /sys/power/pm_trace_dev_match + For example, the above happens to be the VGA device on my EVO, which I used to run with "radeonfb" (it's an ATI Radeon mobility). It turns out that "radeonfb" simply cannot resume that device - it tries to set the diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c index 17e24e3f4422..9f4258df4cfd 100644 --- a/drivers/base/power/trace.c +++ b/drivers/base/power/trace.c @@ -207,6 +207,37 @@ static int show_dev_hash(unsigned int value) static unsigned int hash_value_early_read; +int show_trace_dev_match(char *buf, size_t size) +{ + unsigned int value = hash_value_early_read / (USERHASH * FILEHASH); + int ret = 0; + struct list_head *entry; + + /* + * It's possible that multiple devices will match the hash and we can't + * tell which is the culprit, so it's best to output them all. + */ + device_pm_lock(); + entry = dpm_list.prev; + while (size && entry != &dpm_list) { + struct device *dev = to_device(entry); + unsigned int hash = hash_string(DEVSEED, dev_name(dev), + DEVHASH); + if (hash == value) { + int len = snprintf(buf, size, "%s\n", + dev_driver_string(dev)); + if (len > size) + len = size; + buf += len; + ret += len; + size -= len; + } + entry = entry->prev; + } + device_pm_unlock(); + return ret; +} + static int early_resume_init(void) { hash_value_early_read = read_magic_time(); diff --git a/include/linux/resume-trace.h b/include/linux/resume-trace.h index bc8c3881c729..f31db2368782 100644 --- a/include/linux/resume-trace.h +++ b/include/linux/resume-trace.h @@ -3,6 +3,7 @@ #ifdef CONFIG_PM_TRACE #include +#include extern int pm_trace_enabled; @@ -14,6 +15,7 @@ static inline int pm_trace_is_enabled(void) struct device; extern void set_trace_device(struct device *); extern void generate_resume_trace(const void *tracedata, unsigned int user); +extern int show_trace_dev_match(char *buf, size_t size); #define TRACE_DEVICE(dev) do { \ if (pm_trace_enabled) \ diff --git a/kernel/power/main.c b/kernel/power/main.c index 6b12a0cf4d9f..7b5db6a8561e 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -281,12 +281,30 @@ pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr, } power_attr(pm_trace); + +static ssize_t pm_trace_dev_match_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return show_trace_dev_match(buf, PAGE_SIZE); +} + +static ssize_t +pm_trace_dev_match_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + return -EINVAL; +} + +power_attr(pm_trace_dev_match); + #endif /* CONFIG_PM_TRACE */ static struct attribute * g[] = { &state_attr.attr, #ifdef CONFIG_PM_TRACE &pm_trace_attr.attr, + &pm_trace_dev_match_attr.attr, #endif #ifdef CONFIG_PM_SLEEP &pm_async_attr.attr, -- cgit v1.2.3-70-g09d2 From e1f60b292ffd61151403327aa19ff7a1871820bd Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Wed, 13 Oct 2010 00:13:10 +0200 Subject: PM: Introduce library for device-specific OPPs (v7) SoCs have a standard set of tuples consisting of frequency and voltage pairs that the device will support per voltage domain. These are called Operating Performance Points or OPPs. The actual definitions of OPP varies over silicon versions. For a specific domain, we can have a set of {frequency, voltage} pairs. As the kernel boots and more information is available, a default set of these are activated based on the precise nature of device. Further on operation, based on conditions prevailing in the system (such as temperature), some OPP availability may be temporarily controlled by the SoC frameworks. To implement an OPP, some sort of power management support is necessary hence this library depends on CONFIG_PM. Contributions include: Sanjeev Premi for the initial concept: http://patchwork.kernel.org/patch/50998/ Kevin Hilman for converting original design to device-based. Kevin Hilman and Paul Walmsey for cleaning up many of the function abstractions, improvements and data structure handling. Romit Dasgupta for using enums instead of opp pointers. Thara Gopinath, Eduardo Valentin and Vishwanath BS for fixes and cleanups. Linus Walleij for recommending this layer be made generic for usage in other architectures beyond OMAP and ARM. Mark Brown, Andrew Morton, Rafael J. Wysocki, Paul E. McKenney for valuable improvements. Discussions and comments from: http://marc.info/?l=linux-omap&m=126033945313269&w=2 http://marc.info/?l=linux-omap&m=125482970102327&w=2 http://marc.info/?t=125809247500002&r=1&w=2 http://marc.info/?l=linux-omap&m=126025973426007&w=2 http://marc.info/?t=128152609200064&r=1&w=2 http://marc.info/?t=128468723000002&r=1&w=2 incorporated. v1: http://marc.info/?t=128468723000002&r=1&w=2 Signed-off-by: Nishanth Menon Signed-off-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- Documentation/power/00-INDEX | 2 + Documentation/power/opp.txt | 375 ++++++++++++++++++++++++++ drivers/base/power/Makefile | 1 + drivers/base/power/opp.c | 628 +++++++++++++++++++++++++++++++++++++++++++ include/linux/opp.h | 105 ++++++++ kernel/power/Kconfig | 14 + 6 files changed, 1125 insertions(+) create mode 100644 Documentation/power/opp.txt create mode 100644 drivers/base/power/opp.c create mode 100644 include/linux/opp.h (limited to 'Documentation') diff --git a/Documentation/power/00-INDEX b/Documentation/power/00-INDEX index fb742c213c9e..45e9d4a91284 100644 --- a/Documentation/power/00-INDEX +++ b/Documentation/power/00-INDEX @@ -14,6 +14,8 @@ interface.txt - Power management user interface in /sys/power notifiers.txt - Registering suspend notifiers in device drivers +opp.txt + - Operating Performance Point library pci.txt - How the PCI Subsystem Does Power Management pm_qos_interface.txt diff --git a/Documentation/power/opp.txt b/Documentation/power/opp.txt new file mode 100644 index 000000000000..44d87ad3cea9 --- /dev/null +++ b/Documentation/power/opp.txt @@ -0,0 +1,375 @@ +*=============* +* OPP Library * +*=============* + +(C) 2009-2010 Nishanth Menon , Texas Instruments Incorporated + +Contents +-------- +1. Introduction +2. Initial OPP List Registration +3. OPP Search Functions +4. OPP Availability Control Functions +5. OPP Data Retrieval Functions +6. Cpufreq Table Generation +7. Data Structures + +1. Introduction +=============== +Complex SoCs of today consists of a multiple sub-modules working in conjunction. +In an operational system executing varied use cases, not all modules in the SoC +need to function at their highest performing frequency all the time. To +facilitate this, sub-modules in a SoC are grouped into domains, allowing some +domains to run at lower voltage and frequency while other domains are loaded +more. The set of discrete tuples consisting of frequency and voltage pairs that +the device will support per domain are called Operating Performance Points or +OPPs. + +OPP library provides a set of helper functions to organize and query the OPP +information. The library is located in drivers/base/power/opp.c and the header +is located in include/linux/opp.h. OPP library can be enabled by enabling +CONFIG_PM_OPP from power management menuconfig menu. OPP library depends on +CONFIG_PM as certain SoCs such as Texas Instrument's OMAP framework allows to +optionally boot at a certain OPP without needing cpufreq. + +Typical usage of the OPP library is as follows: +(users) -> registers a set of default OPPs -> (library) +SoC framework -> modifies on required cases certain OPPs -> OPP layer + -> queries to search/retrieve information -> + +OPP layer expects each domain to be represented by a unique device pointer. SoC +framework registers a set of initial OPPs per device with the OPP layer. This +list is expected to be an optimally small number typically around 5 per device. +This initial list contains a set of OPPs that the framework expects to be safely +enabled by default in the system. + +Note on OPP Availability: +------------------------ +As the system proceeds to operate, SoC framework may choose to make certain +OPPs available or not available on each device based on various external +factors. Example usage: Thermal management or other exceptional situations where +SoC framework might choose to disable a higher frequency OPP to safely continue +operations until that OPP could be re-enabled if possible. + +OPP library facilitates this concept in it's implementation. The following +operational functions operate only on available opps: +opp_find_freq_{ceil, floor}, opp_get_voltage, opp_get_freq, opp_get_opp_count +and opp_init_cpufreq_table + +opp_find_freq_exact is meant to be used to find the opp pointer which can then +be used for opp_enable/disable functions to make an opp available as required. + +WARNING: Users of OPP library should refresh their availability count using +get_opp_count if opp_enable/disable functions are invoked for a device, the +exact mechanism to trigger these or the notification mechanism to other +dependent subsystems such as cpufreq are left to the discretion of the SoC +specific framework which uses the OPP library. Similar care needs to be taken +care to refresh the cpufreq table in cases of these operations. + +WARNING on OPP List locking mechanism: +------------------------------------------------- +OPP library uses RCU for exclusivity. RCU allows the query functions to operate +in multiple contexts and this synchronization mechanism is optimal for a read +intensive operations on data structure as the OPP library caters to. + +To ensure that the data retrieved are sane, the users such as SoC framework +should ensure that the section of code operating on OPP queries are locked +using RCU read locks. The opp_find_freq_{exact,ceil,floor}, +opp_get_{voltage, freq, opp_count} fall into this category. + +opp_{add,enable,disable} are updaters which use mutex and implement it's own +RCU locking mechanisms. opp_init_cpufreq_table acts as an updater and uses +mutex to implment RCU updater strategy. These functions should *NOT* be called +under RCU locks and other contexts that prevent blocking functions in RCU or +mutex operations from working. + +2. Initial OPP List Registration +================================ +The SoC implementation calls opp_add function iteratively to add OPPs per +device. It is expected that the SoC framework will register the OPP entries +optimally- typical numbers range to be less than 5. The list generated by +registering the OPPs is maintained by OPP library throughout the device +operation. The SoC framework can subsequently control the availability of the +OPPs dynamically using the opp_enable / disable functions. + +opp_add - Add a new OPP for a specific domain represented by the device pointer. + The OPP is defined using the frequency and voltage. Once added, the OPP + is assumed to be available and control of it's availability can be done + with the opp_enable/disable functions. OPP library internally stores + and manages this information in the opp struct. This function may be + used by SoC framework to define a optimal list as per the demands of + SoC usage environment. + + WARNING: Do not use this function in interrupt context. + + Example: + soc_pm_init() + { + /* Do things */ + r = opp_add(mpu_dev, 1000000, 900000); + if (!r) { + pr_err("%s: unable to register mpu opp(%d)\n", r); + goto no_cpufreq; + } + /* Do cpufreq things */ + no_cpufreq: + /* Do remaining things */ + } + +3. OPP Search Functions +======================= +High level framework such as cpufreq operates on frequencies. To map the +frequency back to the corresponding OPP, OPP library provides handy functions +to search the OPP list that OPP library internally manages. These search +functions return the matching pointer representing the opp if a match is +found, else returns error. These errors are expected to be handled by standard +error checks such as IS_ERR() and appropriate actions taken by the caller. + +opp_find_freq_exact - Search for an OPP based on an *exact* frequency and + availability. This function is especially useful to enable an OPP which + is not available by default. + Example: In a case when SoC framework detects a situation where a + higher frequency could be made available, it can use this function to + find the OPP prior to call the opp_enable to actually make it available. + rcu_read_lock(); + opp = opp_find_freq_exact(dev, 1000000000, false); + rcu_read_unlock(); + /* dont operate on the pointer.. just do a sanity check.. */ + if (IS_ERR(opp)) { + pr_err("frequency not disabled!\n"); + /* trigger appropriate actions.. */ + } else { + opp_enable(dev,1000000000); + } + + NOTE: This is the only search function that operates on OPPs which are + not available. + +opp_find_freq_floor - Search for an available OPP which is *at most* the + provided frequency. This function is useful while searching for a lesser + match OR operating on OPP information in the order of decreasing + frequency. + Example: To find the highest opp for a device: + freq = ULONG_MAX; + rcu_read_lock(); + opp_find_freq_floor(dev, &freq); + rcu_read_unlock(); + +opp_find_freq_ceil - Search for an available OPP which is *at least* the + provided frequency. This function is useful while searching for a + higher match OR operating on OPP information in the order of increasing + frequency. + Example 1: To find the lowest opp for a device: + freq = 0; + rcu_read_lock(); + opp_find_freq_ceil(dev, &freq); + rcu_read_unlock(); + Example 2: A simplified implementation of a SoC cpufreq_driver->target: + soc_cpufreq_target(..) + { + /* Do stuff like policy checks etc. */ + /* Find the best frequency match for the req */ + rcu_read_lock(); + opp = opp_find_freq_ceil(dev, &freq); + rcu_read_unlock(); + if (!IS_ERR(opp)) + soc_switch_to_freq_voltage(freq); + else + /* do something when we cant satisfy the req */ + /* do other stuff */ + } + +4. OPP Availability Control Functions +===================================== +A default OPP list registered with the OPP library may not cater to all possible +situation. The OPP library provides a set of functions to modify the +availability of a OPP within the OPP list. This allows SoC frameworks to have +fine grained dynamic control of which sets of OPPs are operationally available. +These functions are intended to *temporarily* remove an OPP in conditions such +as thermal considerations (e.g. don't use OPPx until the temperature drops). + +WARNING: Do not use these functions in interrupt context. + +opp_enable - Make a OPP available for operation. + Example: Lets say that 1GHz OPP is to be made available only if the + SoC temperature is lower than a certain threshold. The SoC framework + implementation might choose to do something as follows: + if (cur_temp < temp_low_thresh) { + /* Enable 1GHz if it was disabled */ + rcu_read_lock(); + opp = opp_find_freq_exact(dev, 1000000000, false); + rcu_read_unlock(); + /* just error check */ + if (!IS_ERR(opp)) + ret = opp_enable(dev, 1000000000); + else + goto try_something_else; + } + +opp_disable - Make an OPP to be not available for operation + Example: Lets say that 1GHz OPP is to be disabled if the temperature + exceeds a threshold value. The SoC framework implementation might + choose to do something as follows: + if (cur_temp > temp_high_thresh) { + /* Disable 1GHz if it was enabled */ + rcu_read_lock(); + opp = opp_find_freq_exact(dev, 1000000000, true); + rcu_read_unlock(); + /* just error check */ + if (!IS_ERR(opp)) + ret = opp_disable(dev, 1000000000); + else + goto try_something_else; + } + +5. OPP Data Retrieval Functions +=============================== +Since OPP library abstracts away the OPP information, a set of functions to pull +information from the OPP structure is necessary. Once an OPP pointer is +retrieved using the search functions, the following functions can be used by SoC +framework to retrieve the information represented inside the OPP layer. + +opp_get_voltage - Retrieve the voltage represented by the opp pointer. + Example: At a cpufreq transition to a different frequency, SoC + framework requires to set the voltage represented by the OPP using + the regulator framework to the Power Management chip providing the + voltage. + soc_switch_to_freq_voltage(freq) + { + /* do things */ + rcu_read_lock(); + opp = opp_find_freq_ceil(dev, &freq); + v = opp_get_voltage(opp); + rcu_read_unlock(); + if (v) + regulator_set_voltage(.., v); + /* do other things */ + } + +opp_get_freq - Retrieve the freq represented by the opp pointer. + Example: Lets say the SoC framework uses a couple of helper functions + we could pass opp pointers instead of doing additional parameters to + handle quiet a bit of data parameters. + soc_cpufreq_target(..) + { + /* do things.. */ + max_freq = ULONG_MAX; + rcu_read_lock(); + max_opp = opp_find_freq_floor(dev,&max_freq); + requested_opp = opp_find_freq_ceil(dev,&freq); + if (!IS_ERR(max_opp) && !IS_ERR(requested_opp)) + r = soc_test_validity(max_opp, requested_opp); + rcu_read_unlock(); + /* do other things */ + } + soc_test_validity(..) + { + if(opp_get_voltage(max_opp) < opp_get_voltage(requested_opp)) + return -EINVAL; + if(opp_get_freq(max_opp) < opp_get_freq(requested_opp)) + return -EINVAL; + /* do things.. */ + } + +opp_get_opp_count - Retrieve the number of available opps for a device + Example: Lets say a co-processor in the SoC needs to know the available + frequencies in a table, the main processor can notify as following: + soc_notify_coproc_available_frequencies() + { + /* Do things */ + rcu_read_lock(); + num_available = opp_get_opp_count(dev); + speeds = kzalloc(sizeof(u32) * num_available, GFP_KERNEL); + /* populate the table in increasing order */ + freq = 0; + while (!IS_ERR(opp = opp_find_freq_ceil(dev, &freq))) { + speeds[i] = freq; + freq++; + i++; + } + rcu_read_unlock(); + + soc_notify_coproc(AVAILABLE_FREQs, speeds, num_available); + /* Do other things */ + } + +6. Cpufreq Table Generation +=========================== +opp_init_cpufreq_table - cpufreq framework typically is initialized with + cpufreq_frequency_table_cpuinfo which is provided with the list of + frequencies that are available for operation. This function provides + a ready to use conversion routine to translate the OPP layer's internal + information about the available frequencies into a format readily + providable to cpufreq. + + WARNING: Do not use this function in interrupt context. + + Example: + soc_pm_init() + { + /* Do things */ + r = opp_init_cpufreq_table(dev, &freq_table); + if (!r) + cpufreq_frequency_table_cpuinfo(policy, freq_table); + /* Do other things */ + } + + NOTE: This function is available only if CONFIG_CPU_FREQ is enabled in + addition to CONFIG_PM as power management feature is required to + dynamically scale voltage and frequency in a system. + +7. Data Structures +================== +Typically an SoC contains multiple voltage domains which are variable. Each +domain is represented by a device pointer. The relationship to OPP can be +represented as follows: +SoC + |- device 1 + | |- opp 1 (availability, freq, voltage) + | |- opp 2 .. + ... ... + | `- opp n .. + |- device 2 + ... + `- device m + +OPP library maintains a internal list that the SoC framework populates and +accessed by various functions as described above. However, the structures +representing the actual OPPs and domains are internal to the OPP library itself +to allow for suitable abstraction reusable across systems. + +struct opp - The internal data structure of OPP library which is used to + represent an OPP. In addition to the freq, voltage, availability + information, it also contains internal book keeping information required + for the OPP library to operate on. Pointer to this structure is + provided back to the users such as SoC framework to be used as a + identifier for OPP in the interactions with OPP layer. + + WARNING: The struct opp pointer should not be parsed or modified by the + users. The defaults of for an instance is populated by opp_add, but the + availability of the OPP can be modified by opp_enable/disable functions. + +struct device - This is used to identify a domain to the OPP layer. The + nature of the device and it's implementation is left to the user of + OPP library such as the SoC framework. + +Overall, in a simplistic view, the data structure operations is represented as +following: + +Initialization / modification: + +-----+ /- opp_enable +opp_add --> | opp | <------- + | +-----+ \- opp_disable + \-------> domain_info(device) + +Search functions: + /-- opp_find_freq_ceil ---\ +-----+ +domain_info<---- opp_find_freq_exact -----> | opp | + \-- opp_find_freq_floor ---/ +-----+ + +Retrieval functions: ++-----+ /- opp_get_voltage +| opp | <--- ++-----+ \- opp_get_freq + +domain_info <- opp_get_opp_count diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index cbccf9a3cee4..abe46edfe5b4 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_OPS) += generic_ops.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o +obj-$(CONFIG_PM_OPP) += opp.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG ccflags-$(CONFIG_PM_VERBOSE) += -DDEBUG diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c new file mode 100644 index 000000000000..2bb9b4cf59d7 --- /dev/null +++ b/drivers/base/power/opp.c @@ -0,0 +1,628 @@ +/* + * Generic OPP Interface + * + * Copyright (C) 2009-2010 Texas Instruments Incorporated. + * Nishanth Menon + * Romit Dasgupta + * Kevin Hilman + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Internal data structure organization with the OPP layer library is as + * follows: + * dev_opp_list (root) + * |- device 1 (represents voltage domain 1) + * | |- opp 1 (availability, freq, voltage) + * | |- opp 2 .. + * ... ... + * | `- opp n .. + * |- device 2 (represents the next voltage domain) + * ... + * `- device m (represents mth voltage domain) + * device 1, 2.. are represented by dev_opp structure while each opp + * is represented by the opp structure. + */ + +/** + * struct opp - Generic OPP description structure + * @node: opp list node. The nodes are maintained throughout the lifetime + * of boot. It is expected only an optimal set of OPPs are + * added to the library by the SoC framework. + * RCU usage: opp list is traversed with RCU locks. node + * modification is possible realtime, hence the modifications + * are protected by the dev_opp_list_lock for integrity. + * IMPORTANT: the opp nodes should be maintained in increasing + * order. + * @available: true/false - marks if this OPP as available or not + * @rate: Frequency in hertz + * @u_volt: Nominal voltage in microvolts corresponding to this OPP + * @dev_opp: points back to the device_opp struct this opp belongs to + * + * This structure stores the OPP information for a given device. + */ +struct opp { + struct list_head node; + + bool available; + unsigned long rate; + unsigned long u_volt; + + struct device_opp *dev_opp; +}; + +/** + * struct device_opp - Device opp structure + * @node: list node - contains the devices with OPPs that + * have been registered. Nodes once added are not modified in this + * list. + * RCU usage: nodes are not modified in the list of device_opp, + * however addition is possible and is secured by dev_opp_list_lock + * @dev: device pointer + * @opp_list: list of opps + * + * This is an internal data structure maintaining the link to opps attached to + * a device. This structure is not meant to be shared to users as it is + * meant for book keeping and private to OPP library + */ +struct device_opp { + struct list_head node; + + struct device *dev; + struct list_head opp_list; +}; + +/* + * The root of the list of all devices. All device_opp structures branch off + * from here, with each device_opp containing the list of opp it supports in + * various states of availability. + */ +static LIST_HEAD(dev_opp_list); +/* Lock to allow exclusive modification to the device and opp lists */ +static DEFINE_MUTEX(dev_opp_list_lock); + +/** + * find_device_opp() - find device_opp struct using device pointer + * @dev: device pointer used to lookup device OPPs + * + * Search list of device OPPs for one containing matching device. Does a RCU + * reader operation to grab the pointer needed. + * + * Returns pointer to 'struct device_opp' if found, otherwise -ENODEV or + * -EINVAL based on type of error. + * + * Locking: This function must be called under rcu_read_lock(). device_opp + * is a RCU protected pointer. This means that device_opp is valid as long + * as we are under RCU lock. + */ +static struct device_opp *find_device_opp(struct device *dev) +{ + struct device_opp *tmp_dev_opp, *dev_opp = ERR_PTR(-ENODEV); + + if (unlikely(IS_ERR_OR_NULL(dev))) { + pr_err("%s: Invalid parameters\n", __func__); + return ERR_PTR(-EINVAL); + } + + list_for_each_entry_rcu(tmp_dev_opp, &dev_opp_list, node) { + if (tmp_dev_opp->dev == dev) { + dev_opp = tmp_dev_opp; + break; + } + } + + return dev_opp; +} + +/** + * opp_get_voltage() - Gets the voltage corresponding to an available opp + * @opp: opp for which voltage has to be returned for + * + * Return voltage in micro volt corresponding to the opp, else + * return 0 + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. This means that opp which could have been fetched by + * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are + * under RCU lock. The pointer returned by the opp_find_freq family must be + * used in the same section as the usage of this function with the pointer + * prior to unlocking with rcu_read_unlock() to maintain the integrity of the + * pointer. + */ +unsigned long opp_get_voltage(struct opp *opp) +{ + struct opp *tmp_opp; + unsigned long v = 0; + + tmp_opp = rcu_dereference(opp); + if (unlikely(IS_ERR_OR_NULL(tmp_opp)) || !tmp_opp->available) + pr_err("%s: Invalid parameters\n", __func__); + else + v = tmp_opp->u_volt; + + return v; +} + +/** + * opp_get_freq() - Gets the frequency corresponding to an available opp + * @opp: opp for which frequency has to be returned for + * + * Return frequency in hertz corresponding to the opp, else + * return 0 + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. This means that opp which could have been fetched by + * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are + * under RCU lock. The pointer returned by the opp_find_freq family must be + * used in the same section as the usage of this function with the pointer + * prior to unlocking with rcu_read_unlock() to maintain the integrity of the + * pointer. + */ +unsigned long opp_get_freq(struct opp *opp) +{ + struct opp *tmp_opp; + unsigned long f = 0; + + tmp_opp = rcu_dereference(opp); + if (unlikely(IS_ERR_OR_NULL(tmp_opp)) || !tmp_opp->available) + pr_err("%s: Invalid parameters\n", __func__); + else + f = tmp_opp->rate; + + return f; +} + +/** + * opp_get_opp_count() - Get number of opps available in the opp list + * @dev: device for which we do this operation + * + * This function returns the number of available opps if there are any, + * else returns 0 if none or the corresponding error value. + * + * Locking: This function must be called under rcu_read_lock(). This function + * internally references two RCU protected structures: device_opp and opp which + * are safe as long as we are under a common RCU locked section. + */ +int opp_get_opp_count(struct device *dev) +{ + struct device_opp *dev_opp; + struct opp *temp_opp; + int count = 0; + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) { + int r = PTR_ERR(dev_opp); + dev_err(dev, "%s: device OPP not found (%d)\n", __func__, r); + return r; + } + + list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->available) + count++; + } + + return count; +} + +/** + * opp_find_freq_exact() - search for an exact frequency + * @dev: device for which we do this operation + * @freq: frequency to search for + * @is_available: true/false - match for available opp + * + * Searches for exact match in the opp list and returns pointer to the matching + * opp if found, else returns ERR_PTR in case of error and should be handled + * using IS_ERR. + * + * Note: available is a modifier for the search. if available=true, then the + * match is for exact matching frequency and is available in the stored OPP + * table. if false, the match is for exact frequency which is not available. + * + * This provides a mechanism to enable an opp which is not available currently + * or the opposite as well. + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. The reason for the same is that the opp pointer which is + * returned will remain valid for use with opp_get_{voltage, freq} only while + * under the locked area. The pointer returned must be used prior to unlocking + * with rcu_read_unlock() to maintain the integrity of the pointer. + */ +struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq, + bool available) +{ + struct device_opp *dev_opp; + struct opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) { + int r = PTR_ERR(dev_opp); + dev_err(dev, "%s: device OPP not found (%d)\n", __func__, r); + return ERR_PTR(r); + } + + list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->available == available && + temp_opp->rate == freq) { + opp = temp_opp; + break; + } + } + + return opp; +} + +/** + * opp_find_freq_ceil() - Search for an rounded ceil freq + * @dev: device for which we do this operation + * @freq: Start frequency + * + * Search for the matching ceil *available* OPP from a starting freq + * for a device. + * + * Returns matching *opp and refreshes *freq accordingly, else returns + * ERR_PTR in case of error and should be handled using IS_ERR. + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. The reason for the same is that the opp pointer which is + * returned will remain valid for use with opp_get_{voltage, freq} only while + * under the locked area. The pointer returned must be used prior to unlocking + * with rcu_read_unlock() to maintain the integrity of the pointer. + */ +struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq) +{ + struct device_opp *dev_opp; + struct opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + if (!dev || !freq) { + dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); + return ERR_PTR(-EINVAL); + } + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return opp; + + list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->available && temp_opp->rate >= *freq) { + opp = temp_opp; + *freq = opp->rate; + break; + } + } + + return opp; +} + +/** + * opp_find_freq_floor() - Search for a rounded floor freq + * @dev: device for which we do this operation + * @freq: Start frequency + * + * Search for the matching floor *available* OPP from a starting freq + * for a device. + * + * Returns matching *opp and refreshes *freq accordingly, else returns + * ERR_PTR in case of error and should be handled using IS_ERR. + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. The reason for the same is that the opp pointer which is + * returned will remain valid for use with opp_get_{voltage, freq} only while + * under the locked area. The pointer returned must be used prior to unlocking + * with rcu_read_unlock() to maintain the integrity of the pointer. + */ +struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq) +{ + struct device_opp *dev_opp; + struct opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + if (!dev || !freq) { + dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); + return ERR_PTR(-EINVAL); + } + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return opp; + + list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->available) { + /* go to the next node, before choosing prev */ + if (temp_opp->rate > *freq) + break; + else + opp = temp_opp; + } + } + if (!IS_ERR(opp)) + *freq = opp->rate; + + return opp; +} + +/** + * opp_add() - Add an OPP table from a table definitions + * @dev: device for which we do this operation + * @freq: Frequency in Hz for this OPP + * @u_volt: Voltage in uVolts for this OPP + * + * This function adds an opp definition to the opp list and returns status. + * The opp is made available by default and it can be controlled using + * opp_enable/disable functions. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) +{ + struct device_opp *dev_opp = NULL; + struct opp *opp, *new_opp; + struct list_head *head; + + /* allocate new OPP node */ + new_opp = kzalloc(sizeof(struct opp), GFP_KERNEL); + if (!new_opp) { + dev_warn(dev, "%s: Unable to create new OPP node\n", __func__); + return -ENOMEM; + } + + /* Hold our list modification lock here */ + mutex_lock(&dev_opp_list_lock); + + /* Check for existing list for 'dev' */ + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) { + /* + * Allocate a new device OPP table. In the infrequent case + * where a new device is needed to be added, we pay this + * penalty. + */ + dev_opp = kzalloc(sizeof(struct device_opp), GFP_KERNEL); + if (!dev_opp) { + mutex_unlock(&dev_opp_list_lock); + kfree(new_opp); + dev_warn(dev, + "%s: Unable to create device OPP structure\n", + __func__); + return -ENOMEM; + } + + dev_opp->dev = dev; + INIT_LIST_HEAD(&dev_opp->opp_list); + + /* Secure the device list modification */ + list_add_rcu(&dev_opp->node, &dev_opp_list); + } + + /* populate the opp table */ + new_opp->dev_opp = dev_opp; + new_opp->rate = freq; + new_opp->u_volt = u_volt; + new_opp->available = true; + + /* Insert new OPP in order of increasing frequency */ + head = &dev_opp->opp_list; + list_for_each_entry_rcu(opp, &dev_opp->opp_list, node) { + if (new_opp->rate < opp->rate) + break; + else + head = &opp->node; + } + + list_add_rcu(&new_opp->node, head); + mutex_unlock(&dev_opp_list_lock); + + return 0; +} + +/** + * opp_set_availability() - helper to set the availability of an opp + * @dev: device for which we do this operation + * @freq: OPP frequency to modify availability + * @availability_req: availability status requested for this opp + * + * Set the availability of an OPP with an RCU operation, opp_{enable,disable} + * share a common logic which is isolated here. + * + * Returns -EINVAL for bad pointers, -ENOMEM if no memory available for the + * copy operation, returns 0 if no modifcation was done OR modification was + * successful. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks to + * keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex locking or synchronize_rcu() blocking calls cannot be used. + */ +static int opp_set_availability(struct device *dev, unsigned long freq, + bool availability_req) +{ + struct device_opp *tmp_dev_opp, *dev_opp = NULL; + struct opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV); + int r = 0; + + /* keep the node allocated */ + new_opp = kmalloc(sizeof(struct opp), GFP_KERNEL); + if (!new_opp) { + dev_warn(dev, "%s: Unable to create OPP\n", __func__); + return -ENOMEM; + } + + mutex_lock(&dev_opp_list_lock); + + /* Find the device_opp */ + list_for_each_entry(tmp_dev_opp, &dev_opp_list, node) { + if (dev == tmp_dev_opp->dev) { + dev_opp = tmp_dev_opp; + break; + } + } + if (IS_ERR(dev_opp)) { + r = PTR_ERR(dev_opp); + dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r); + goto unlock; + } + + /* Do we have the frequency? */ + list_for_each_entry(tmp_opp, &dev_opp->opp_list, node) { + if (tmp_opp->rate == freq) { + opp = tmp_opp; + break; + } + } + if (IS_ERR(opp)) { + r = PTR_ERR(opp); + goto unlock; + } + + /* Is update really needed? */ + if (opp->available == availability_req) + goto unlock; + /* copy the old data over */ + *new_opp = *opp; + + /* plug in new node */ + new_opp->available = availability_req; + + list_replace_rcu(&opp->node, &new_opp->node); + mutex_unlock(&dev_opp_list_lock); + synchronize_rcu(); + + /* clean up old opp */ + new_opp = opp; + goto out; + +unlock: + mutex_unlock(&dev_opp_list_lock); +out: + kfree(new_opp); + return r; +} + +/** + * opp_enable() - Enable a specific OPP + * @dev: device for which we do this operation + * @freq: OPP frequency to enable + * + * Enables a provided opp. If the operation is valid, this returns 0, else the + * corresponding error value. It is meant to be used for users an OPP available + * after being temporarily made unavailable with opp_disable. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function indirectly uses RCU and mutex locks to keep the + * integrity of the internal data structures. Callers should ensure that + * this function is *NOT* called under RCU protection or in contexts where + * mutex locking or synchronize_rcu() blocking calls cannot be used. + */ +int opp_enable(struct device *dev, unsigned long freq) +{ + return opp_set_availability(dev, freq, true); +} + +/** + * opp_disable() - Disable a specific OPP + * @dev: device for which we do this operation + * @freq: OPP frequency to disable + * + * Disables a provided opp. If the operation is valid, this returns + * 0, else the corresponding error value. It is meant to be a temporary + * control by users to make this OPP not available until the circumstances are + * right to make it available again (with a call to opp_enable). + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function indirectly uses RCU and mutex locks to keep the + * integrity of the internal data structures. Callers should ensure that + * this function is *NOT* called under RCU protection or in contexts where + * mutex locking or synchronize_rcu() blocking calls cannot be used. + */ +int opp_disable(struct device *dev, unsigned long freq) +{ + return opp_set_availability(dev, freq, false); +} + +#ifdef CONFIG_CPU_FREQ +/** + * opp_init_cpufreq_table() - create a cpufreq table for a device + * @dev: device for which we do this operation + * @table: Cpufreq table returned back to caller + * + * Generate a cpufreq table for a provided device- this assumes that the + * opp list is already initialized and ready for usage. + * + * This function allocates required memory for the cpufreq table. It is + * expected that the caller does the required maintenance such as freeing + * the table as required. + * + * Returns -EINVAL for bad pointers, -ENODEV if the device is not found, -ENOMEM + * if no memory available for the operation (table is not populated), returns 0 + * if successful and table is populated. + * + * WARNING: It is important for the callers to ensure refreshing their copy of + * the table if any of the mentioned functions have been invoked in the interim. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * To simplify the logic, we pretend we are updater and hold relevant mutex here + * Callers should ensure that this function is *NOT* called under RCU protection + * or in contexts where mutex locking cannot be used. + */ +int opp_init_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table) +{ + struct device_opp *dev_opp; + struct opp *opp; + struct cpufreq_frequency_table *freq_table; + int i = 0; + + /* Pretend as if I am an updater */ + mutex_lock(&dev_opp_list_lock); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) { + int r = PTR_ERR(dev_opp); + mutex_unlock(&dev_opp_list_lock); + dev_err(dev, "%s: Device OPP not found (%d)\n", __func__, r); + return r; + } + + freq_table = kzalloc(sizeof(struct cpufreq_frequency_table) * + (opp_get_opp_count(dev) + 1), GFP_KERNEL); + if (!freq_table) { + mutex_unlock(&dev_opp_list_lock); + dev_warn(dev, "%s: Unable to allocate frequency table\n", + __func__); + return -ENOMEM; + } + + list_for_each_entry(opp, &dev_opp->opp_list, node) { + if (opp->available) { + freq_table[i].index = i; + freq_table[i].frequency = opp->rate / 1000; + i++; + } + } + mutex_unlock(&dev_opp_list_lock); + + freq_table[i].index = i; + freq_table[i].frequency = CPUFREQ_TABLE_END; + + *table = &freq_table[0]; + + return 0; +} +#endif /* CONFIG_CPU_FREQ */ diff --git a/include/linux/opp.h b/include/linux/opp.h new file mode 100644 index 000000000000..5449945d589f --- /dev/null +++ b/include/linux/opp.h @@ -0,0 +1,105 @@ +/* + * Generic OPP Interface + * + * Copyright (C) 2009-2010 Texas Instruments Incorporated. + * Nishanth Menon + * Romit Dasgupta + * Kevin Hilman + * + * 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. + */ + +#ifndef __LINUX_OPP_H__ +#define __LINUX_OPP_H__ + +#include +#include + +struct opp; + +#if defined(CONFIG_PM_OPP) + +unsigned long opp_get_voltage(struct opp *opp); + +unsigned long opp_get_freq(struct opp *opp); + +int opp_get_opp_count(struct device *dev); + +struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq, + bool available); + +struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq); + +struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq); + +int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt); + +int opp_enable(struct device *dev, unsigned long freq); + +int opp_disable(struct device *dev, unsigned long freq); + +#else +static inline unsigned long opp_get_voltage(struct opp *opp) +{ + return 0; +} + +static inline unsigned long opp_get_freq(struct opp *opp) +{ + return 0; +} + +static inline int opp_get_opp_count(struct device *dev) +{ + return 0; +} + +static inline struct opp *opp_find_freq_exact(struct device *dev, + unsigned long freq, bool available) +{ + return ERR_PTR(-EINVAL); +} + +static inline struct opp *opp_find_freq_floor(struct device *dev, + unsigned long *freq) +{ + return ERR_PTR(-EINVAL); +} + +static inline struct opp *opp_find_freq_ceil(struct device *dev, + unsigned long *freq) +{ + return ERR_PTR(-EINVAL); +} + +static inline int opp_add(struct device *dev, unsigned long freq, + unsigned long u_volt) +{ + return -EINVAL; +} + +static inline int opp_enable(struct device *dev, unsigned long freq) +{ + return 0; +} + +static inline int opp_disable(struct device *dev, unsigned long freq) +{ + return 0; +} +#endif /* CONFIG_PM */ + +#if defined(CONFIG_CPU_FREQ) && defined(CONFIG_PM_OPP) +int opp_init_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table); +#else +static inline int opp_init_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table) +{ + return -EINVAL; +} +#endif /* CONFIG_CPU_FREQ */ + +#endif /* __LINUX_OPP_H__ */ diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index e45894c696ee..29bff6117abc 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -245,3 +245,17 @@ config PM_OPS bool depends on PM_SLEEP || PM_RUNTIME default y + +config PM_OPP + bool "Operating Performance Point (OPP) Layer library" + depends on PM + ---help--- + SOCs have a standard set of tuples consisting of frequency and + voltage pairs that the device will support per voltage domain. This + is called Operating Performance Point or OPP. The actual definitions + of OPP varies over silicon within the same family of devices. + + OPP layer organizes the data internally using device pointers + representing individual voltage domains and provides SOC + implementations a ready to use framework to manage OPPs. + For more information, read -- cgit v1.2.3-70-g09d2 From e82b8e4ea4f3dffe6e7939f90e78da675fcc450e Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Mon, 4 Oct 2010 17:03:20 -0700 Subject: x86: Add IRQ_TIME_ACCOUNTING This patch adds IRQ_TIME_ACCOUNTING option on x86 and runtime enables it when TSC is enabled. This change just enables fine grained irq time accounting, isn't used yet. Following patches use it for different purposes. Signed-off-by: Venkatesh Pallipadi Signed-off-by: Peter Zijlstra LKML-Reference: <1286237003-12406-6-git-send-email-venki@google.com> Signed-off-by: Ingo Molnar --- Documentation/kernel-parameters.txt | 4 ++++ arch/x86/Kconfig | 11 +++++++++++ arch/x86/kernel/tsc.c | 8 ++++++++ 3 files changed, 23 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 8dd7248508a9..ed05a4a0d242 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2435,6 +2435,10 @@ and is between 256 and 4096 characters. It is defined in the file disables clocksource verification at runtime. Used to enable high-resolution timer mode on older hardware, and in virtualized environment. + [x86] noirqtime: Do not use TSC to do irq accounting. + Used to run time disable IRQ_TIME_ACCOUNTING on any + platforms where RDTSC is slow and this accounting + can add overhead. turbografx.map[2|3]= [HW,JOY] TurboGraFX parallel port interface diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index cea0cd9a316f..f4c70c246ffe 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -795,6 +795,17 @@ config SCHED_MC making when dealing with multi-core CPU chips at a cost of slightly increased overhead in some places. If unsure say N here. +config IRQ_TIME_ACCOUNTING + bool "Fine granularity task level IRQ time accounting" + default n + ---help--- + Select this option to enable fine granularity task irq time + accounting. This is done by reading a timestamp on each + transitions between softirq and hardirq state, so there can be a + small performance impact. + + If in doubt, say N here. + source "kernel/Kconfig.preempt" config X86_UP_APIC diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 26a863a9c2a8..a1c2cd768538 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c @@ -104,10 +104,14 @@ int __init notsc_setup(char *str) __setup("notsc", notsc_setup); +static int no_sched_irq_time; + static int __init tsc_setup(char *str) { if (!strcmp(str, "reliable")) tsc_clocksource_reliable = 1; + if (!strncmp(str, "noirqtime", 9)) + no_sched_irq_time = 1; return 1; } @@ -801,6 +805,7 @@ void mark_tsc_unstable(char *reason) if (!tsc_unstable) { tsc_unstable = 1; sched_clock_stable = 0; + disable_sched_clock_irqtime(); printk(KERN_INFO "Marking TSC unstable due to %s\n", reason); /* Change only the rating, when not registered */ if (clocksource_tsc.mult) @@ -987,6 +992,9 @@ void __init tsc_init(void) /* now allow native_sched_clock() to use rdtsc */ tsc_disabled = 0; + if (!no_sched_irq_time) + enable_sched_clock_irqtime(); + lpj = ((u64)tsc_khz * 1000); do_div(lpj, HZ); lpj_fine = lpj; -- cgit v1.2.3-70-g09d2 From 516c714c6c823e412dc485ca9c5307348cddd097 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 6 Sep 2010 14:05:43 -0300 Subject: V4L/DVB: Add documentation about the Ibmcam/Konica new gspca driver formats Cc: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/pixfmt.xml | 14 ++++++++++++++ Documentation/DocBook/v4l/videodev2.h.xml | 2 ++ 2 files changed, 16 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml index c4ad0a8e42dc..5376aefeeffc 100644 --- a/Documentation/DocBook/v4l/pixfmt.xml +++ b/Documentation/DocBook/v4l/pixfmt.xml @@ -787,6 +787,20 @@ http://www.thedirks.org/winnov/ 'TM60' Used by Trident tm6000 + + V4L2_PIX_FMT_CIT_YYVYUY + 'CITV' + Used by xirlink CIT, found at IBM webcams. + Uses one line of Y then 1 line of VYUY + + + + V4L2_PIX_FMT_KONICA420 + 'KONI' + Used by Konica webcams. + YUV420 planar in blocks of 256 pixels. + + V4L2_PIX_FMT_YYUV 'YYUV' diff --git a/Documentation/DocBook/v4l/videodev2.h.xml b/Documentation/DocBook/v4l/videodev2.h.xml index 865b06d9e679..eda385891ade 100644 --- a/Documentation/DocBook/v4l/videodev2.h.xml +++ b/Documentation/DocBook/v4l/videodev2.h.xml @@ -364,6 +364,8 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */ #define V4L2_PIX_FMT_TM6000 v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */ #define V4L2_PIX_FMT_STV0680 v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */ +#define V4L2_PIX_FMT_CIT_YYVYUY v4l2_fourcc('C', 'I', 'T', 'V') /* one line of Y then 1 line of VYUY */ +#define V4L2_PIX_FMT_KONICA420 v4l2_fourcc('K', 'O', 'N', 'I') /* YUV420 planar in blocks of 256 pixels */ /* * F O R M A T E N U M E R A T I O N -- cgit v1.2.3-70-g09d2 From 692d5522646fdf432329efbe5092dc9c5ca83e85 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 30 Jul 2010 17:24:55 -0300 Subject: V4L/DVB: v4l: Add a v4l2_subdev host private data field The existing priv field stores subdev private data owned by the subdev driver. Host (bridge) drivers might need to store per-subdev host-specific data, such as a pointer to platform data. Add a v4l2_subdev host_priv field to store host-specific data, and rename the existing priv field to dev_priv. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 5 +++++ include/media/v4l2-subdev.h | 20 ++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index e831aaca66f8..f5fdb395287f 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -192,6 +192,11 @@ You also need a way to go from the low-level struct to v4l2_subdev. For the common i2c_client struct the i2c_set_clientdata() call is used to store a v4l2_subdev pointer, for other busses you may have to use other methods. +Bridges might also need to store per-subdev private data, such as a pointer to +bridge-specific per-subdev private data. The v4l2_subdev structure provides +host private data for that purpose that can be accessed with +v4l2_get_subdev_hostdata() and v4l2_set_subdev_hostdata(). + From the bridge driver perspective you load the sub-device module and somehow obtain the v4l2_subdev pointer. For i2c devices this is easy: you call i2c_get_clientdata(). For other busses something similar needs to be done. diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 905879d7742f..b0316a7cf08d 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -438,17 +438,28 @@ struct v4l2_subdev { /* can be used to group similar subdevs, value is driver-specific */ u32 grp_id; /* pointer to private data */ - void *priv; + void *dev_priv; + void *host_priv; }; static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p) { - sd->priv = p; + sd->dev_priv = p; } static inline void *v4l2_get_subdevdata(const struct v4l2_subdev *sd) { - return sd->priv; + return sd->dev_priv; +} + +static inline void v4l2_set_subdev_hostdata(struct v4l2_subdev *sd, void *p) +{ + sd->host_priv = p; +} + +static inline void *v4l2_get_subdev_hostdata(const struct v4l2_subdev *sd) +{ + return sd->host_priv; } static inline void v4l2_subdev_init(struct v4l2_subdev *sd, @@ -462,7 +473,8 @@ static inline void v4l2_subdev_init(struct v4l2_subdev *sd, sd->flags = 0; sd->name[0] = '\0'; sd->grp_id = 0; - sd->priv = NULL; + sd->dev_priv = NULL; + sd->host_priv = NULL; } /* Call an ops of a v4l2_subdev, doing the right checks against -- cgit v1.2.3-70-g09d2 From d2f918bba7a482bee18cc0ede7791f7d846dd5d0 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Thu, 2 Sep 2010 17:29:30 -0300 Subject: V4L/DVB: Support or LME2510(C) DM04/QQBOX USB DVB-S BOXES DM04/QQBOX DVB-S USB BOX with LME2510C+SHARP:BS2F7HZ7395 or LME2510+LGTDQT-P001F tuner. [mchehab@redhat.com: Fix merge conflicts/compilation and CodingStyle issues] Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- Documentation/dvb/get_dvb_firmware | 46 +- Documentation/dvb/lmedm04.txt | 55 ++ drivers/media/IR/keymaps/Makefile | 1 + drivers/media/IR/keymaps/rc-lme2510.c | 68 +++ drivers/media/dvb/dvb-usb/Kconfig | 11 + drivers/media/dvb/dvb-usb/Makefile | 3 + drivers/media/dvb/dvb-usb/lmedm04.c | 936 ++++++++++++++++++++++++++++++++++ drivers/media/dvb/dvb-usb/lmedm04.h | 187 +++++++ include/media/rc-map.h | 1 + 9 files changed, 1307 insertions(+), 1 deletion(-) create mode 100644 Documentation/dvb/lmedm04.txt create mode 100644 drivers/media/IR/keymaps/rc-lme2510.c create mode 100644 drivers/media/dvb/dvb-usb/lmedm04.c create mode 100644 drivers/media/dvb/dvb-usb/lmedm04.h (limited to 'Documentation') diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index 350959f4e41b..59690de8ebfe 100644 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -26,7 +26,8 @@ use IO::Handle; "dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004", "or51211", "or51132_qam", "or51132_vsb", "bluebird", "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718", - "af9015", "ngene", "az6027"); + "af9015", "ngene", "az6027", "lme2510_lg", "lme2510c_s7395", + "lme2510c_s7395_old"); # Check args syntax() if (scalar(@ARGV) != 1); @@ -584,6 +585,49 @@ sub az6027{ $firmware; } + +sub lme2510_lg { + my $sourcefile = "LMEBDA_DVBS.sys"; + my $hash = "fc6017ad01e79890a97ec53bea157ed2"; + my $outfile = "dvb-usb-lme2510-lg.fw"; + my $hasho = "caa065d5fdbd2c09ad57b399bbf55cad"; + + checkstandard(); + + verify($sourcefile, $hash); + extract($sourcefile, 4168, 3841, $outfile); + verify($outfile, $hasho); + $outfile; +} + +sub lme2510c_s7395 { + my $sourcefile = "US2A0D.sys"; + my $hash = "b0155a8083fb822a3bd47bc360e74601"; + my $outfile = "dvb-usb-lme2510c-s7395.fw"; + my $hasho = "3a3cf1aeebd17b6ddc04cebe131e94cf"; + + checkstandard(); + + verify($sourcefile, $hash); + extract($sourcefile, 37248, 3720, $outfile); + verify($outfile, $hasho); + $outfile; +} + +sub lme2510c_s7395_old { + my $sourcefile = "LMEBDA_DVBS7395C.sys"; + my $hash = "7572ae0eb9cdf91baabd7c0ba9e09b31"; + my $outfile = "dvb-usb-lme2510c-s7395.fw"; + my $hasho = "90430c5b435eb5c6f88fd44a9d950674"; + + checkstandard(); + + verify($sourcefile, $hash); + extract($sourcefile, 4208, 3881, $outfile); + verify($outfile, $hasho); + $outfile; +} + # --------------------------------------------------------------- # Utilities diff --git a/Documentation/dvb/lmedm04.txt b/Documentation/dvb/lmedm04.txt new file mode 100644 index 000000000000..4bde457fafc4 --- /dev/null +++ b/Documentation/dvb/lmedm04.txt @@ -0,0 +1,55 @@ +To extract firmware for the DM04/QQBOX you need to copy the +following file(s) to this directory. + +for DM04+/QQBOX LME2510C (Sharp 7395 Tuner) +------------------------------------------- + +The Sharp 7395 driver can be found in windows/system32/driver + +US2A0D.sys (dated 17 Mar 2009) + + +and run +./get_dvb_firmware lme2510c_s7395 + + will produce + dvb-usb-lme2510c-s7395.fw + +An alternative but older firmware can be found on the driver +disk DVB-S_EN_3.5A in BDADriver/driver + +LMEBDA_DVBS7395C.sys (dated 18 Jan 2008) + +and run +./get_dvb_firmware lme2510c_s7395_old + + will produce + dvb-usb-lme2510c-s7395.fw + +-------------------------------------------------------------------- + +The LG firmware can be found on the driver +disk DM04+_5.1A[LG] in BDADriver/driver + +for DM04 LME2510 (LG Tuner) +--------------------------- + +LMEBDA_DVBS.sys (dated 13 Nov 2007) + +and run +./get_dvb_firmware lme2510_lg + + will produce + dvb-usb-lme2510-lg.fw + + +Other LG firmware can be extracted manually from US280D.sys +only found in windows/system32/driver. +However, this firmware does not run very well under Windows +or with the Linux driver. + +dd if=US280D.sys ibs=1 skip=36856 count=3976 of=dvb-usb-lme2510-lg.fw + +--------------------------------------------------------------------- + +Copy the firmware file(s) to /lib/firmware diff --git a/drivers/media/IR/keymaps/Makefile b/drivers/media/IR/keymaps/Makefile index c032b9d2e51a..f755b21eef1a 100644 --- a/drivers/media/IR/keymaps/Makefile +++ b/drivers/media/IR/keymaps/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-kworld-315u.o \ rc-kworld-plus-tv-analog.o \ rc-lirc.o \ + rc-lme2510.o \ rc-manli.o \ rc-msi-tvanywhere.o \ rc-msi-tvanywhere-plus.o \ diff --git a/drivers/media/IR/keymaps/rc-lme2510.c b/drivers/media/IR/keymaps/rc-lme2510.c new file mode 100644 index 000000000000..40dcf0b4e21a --- /dev/null +++ b/drivers/media/IR/keymaps/rc-lme2510.c @@ -0,0 +1,68 @@ +/* LME2510 remote control + * + * + * Copyright (C) 2010 Malcolm Priestley (tvboxspy@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include + + +static struct ir_scancode lme2510_rc[] = { + { 0xba45, KEY_0 }, + { 0xa05f, KEY_1 }, + { 0xaf50, KEY_2 }, + { 0xa25d, KEY_3 }, + { 0xbe41, KEY_4 }, + { 0xf50a, KEY_5 }, + { 0xbd42, KEY_6 }, + { 0xb847, KEY_7 }, + { 0xb649, KEY_8 }, + { 0xfa05, KEY_9 }, + { 0xbc43, KEY_POWER }, + { 0xb946, KEY_SUBTITLE }, + { 0xf906, KEY_PAUSE }, + { 0xfc03, KEY_MEDIA_REPEAT}, + { 0xfd02, KEY_PAUSE }, + { 0xa15e, KEY_VOLUMEUP }, + { 0xa35c, KEY_VOLUMEDOWN }, + { 0xf609, KEY_CHANNELUP }, + { 0xe51a, KEY_CHANNELDOWN }, + { 0xe11e, KEY_PLAY }, + { 0xe41b, KEY_ZOOM }, + { 0xa659, KEY_MUTE }, + { 0xa55a, KEY_TV }, + { 0xe718, KEY_RECORD }, + { 0xf807, KEY_EPG }, + { 0xfe01, KEY_STOP }, + +}; + +static struct rc_keymap lme2510_map = { + .map = { + .scan = lme2510_rc, + .size = ARRAY_SIZE(lme2510_rc), + .ir_type = IR_TYPE_UNKNOWN, + .name = RC_MAP_LME2510, + } +}; + +static int __init init_rc_lme2510_map(void) +{ + return ir_register_map(&lme2510_map); +} + +static void __exit exit_rc_lme2510_map(void) +{ + ir_unregister_map(&lme2510_map); +} + +module_init(init_rc_lme2510_map) +module_exit(exit_rc_lme2510_map) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index c57f828e1eb9..4734ec073e01 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -347,3 +347,14 @@ config DVB_USB_AZ6027 select DVB_STB6100 if !DVB_FE_CUSTOMISE help Say Y here to support the AZ6027 device + +config DVB_USB_LME2510 + tristate "LME DM04/QQBOX DVB-S USB2.0 support" + depends on DVB_USB + select DVB_TDA10086 if !DVB_FE_CUSTOMISE + select DVB_TDA826X if !DVB_FE_CUSTOMISE + select DVB_STV0288 if !DVB_FE_CUSTOMISE + select DVB_IX2505V if !DVB_FE_CUSTOMISE + depends on IR_CORE + help + Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 . diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index 1a192453b0e7..5b1d12f2d591 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -88,6 +88,9 @@ obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o dvb-usb-az6027-objs = az6027.o obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o +dvb-usb-lmedm04-objs = lmedm04.o +obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c new file mode 100644 index 000000000000..d5374ac075d3 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/lmedm04.c @@ -0,0 +1,936 @@ +/* DVB USB compliant linux driver for + * + * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395 + * LME2510 + LGTDQT-P001F + * + * MVB7395 (LME2510C+SHARP:BS2F7HZ7395) + * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V) + * + * MV001F (LME2510 +LGTDQY-P001F) + * LG TDQY - P001F =(TDA8263 + TDA10086H) + * + * For firmware see Documentation/dvb/lmedm04.txt + * + * I2C addresses: + * 0xd0 - STV0288 - Demodulator + * 0xc0 - Sharp IX2505V - Tuner + * --or-- + * 0x1c - TDA10086 - Demodulator + * 0xc0 - TDA8263 - Tuner + * + * ***Please Note*** + * There are other variants of the DM04 + * ***NOT SUPPORTED*** + * MVB0001F (LME2510C+LGTDQT-P001F) + * MV0194 (LME2510+SHARP0194) + * MVB0194 (LME2510C+SHARP0194) + * + * + * VID = 3344 PID LME2510=1122 LME2510C=1120 + * + * Copyright (C) 2010 Malcolm Priestley (tvboxspy@gmail.com) + * LME2510(C)(C) Leaguerme (Shenzhen) MicroElectronics Co., Ltd. + * + * 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. + * + * + * see Documentation/dvb/README.dvb-usb for more information + * + * Known Issues : + * LME2510: Non Intel USB chipsets fail to maintain High Speed on + * Boot or Hot Plug. + * + * DiSEqC functions are not fully supported in this driver. The main + * reason is the frontend is cut off during streaming. Allowing frontend + * access will stall the driver. Applications that attempt to this, the + * commands are ignored. + * + * PID functions have been removed from this driver version due to + * problems with different firmware and application versions. + */ +#define DVB_USB_LOG_PREFIX "LME2510(C)" +#include +#include +#include + +#include "dvb-usb.h" +#include "lmedm04.h" +#include "tda826x.h" +#include "tda10086.h" +#include "stv0288.h" +#include "ix2505v.h" + + + +/* debug */ +static int dvb_usb_lme2510_debug; +#define l_dprintk(var, level, args...) do { \ + if ((var >= level)) \ + printk(KERN_DEBUG DVB_USB_LOG_PREFIX ": " args); \ +} while (0) + +#define deb_info(level, args...) l_dprintk(dvb_usb_lme2510_debug, level, args) +#define debug_data_snipet(level, name, p) \ + deb_info(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \ + *p, *(p+1), *(p+2), *(p+3), *(p+4), \ + *(p+5), *(p+6), *(p+7)); + + +module_param_named(debug, dvb_usb_lme2510_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." + DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +#define TUNER_LG 0x1 +#define TUNER_S7395 0x2 + +struct lme2510_state { + u8 id; + u8 tuner_config; + u8 signal_lock; + u8 signal_level; + u8 signal_sn; + u8 time_key; + u8 i2c_talk_onoff; + u8 i2c_gate; + u8 i2c_tuner_gate_w; + u8 i2c_tuner_gate_r; + u8 i2c_tuner_addr; + void *buffer; + struct urb *lme_urb; + void *usb_buffer; + +}; + +static int lme2510_bulk_write(struct usb_device *dev, + u8 *snd, int len, u8 pipe) +{ + int ret, actual_l; + ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe), + snd, len , &actual_l, 500); + return ret; +} + +static int lme2510_bulk_read(struct usb_device *dev, + u8 *rev, int len, u8 pipe) +{ + int ret, actual_l; + + ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe), + rev, len , &actual_l, 500); + return ret; +} + +static int lme2510_usb_talk(struct dvb_usb_device *d, + u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + struct lme2510_state *st = d->priv; + u8 *buff; + int ret = 0; + + if (st->usb_buffer == NULL) { + st->usb_buffer = kmalloc(512, GFP_KERNEL); + if (st->usb_buffer == NULL) { + info("MEM Error no memory"); + return -ENOMEM; + } + } + buff = st->usb_buffer; + + /* the read/write capped at 512 */ + memcpy(buff, wbuf, (wlen > 512) ? 512 : wlen); + + + ret = mutex_lock_interruptible(&d->usb_mutex); + + if (ret < 0) + return -EAGAIN; + + ret |= usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, 0x01)); + msleep(5); + ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x1); + + msleep(5); + ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x1)); + + msleep(5); + ret |= lme2510_bulk_read(d->udev, buff, (rlen > 512) ? + 512 : rlen , 0x1); + + if (rlen > 0) + memcpy(rbuf, buff, rlen); + + mutex_unlock(&d->usb_mutex); + + return (ret < 0) ? -ENODEV : 0; +} + +static int lme2510_remote_keypress(struct dvb_usb_adapter *adap, u16 keypress) +{ + struct dvb_usb_device *d = adap->dev; + + deb_info(1, "INT Key Keypress =%04x", keypress); + + if (keypress > 0) + ir_keydown(d->rc_input_dev, keypress, 0); + + return 0; +} + +static void lme2510_int_response(struct urb *lme_urb) +{ + struct dvb_usb_adapter *adap = lme_urb->context; + struct lme2510_state *st = adap->dev->priv; + static u8 *ibuf, *rbuf; + int i = 0, offset; + + switch (lme_urb->status) { + case 0: + case -ETIMEDOUT: + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + default: + info("Error %x", lme_urb->status); + break; + } + + rbuf = (u8 *) lme_urb->transfer_buffer; + + offset = ((lme_urb->actual_length/8) > 4) + ? 4 : (lme_urb->actual_length/8) ; + + + for (i = 0; i < offset; ++i) { + ibuf = (u8 *)&rbuf[i*8]; + deb_info(5, "INT O/S C =%02x C/O=%02x Type =%02x%02x", + offset, i, ibuf[0], ibuf[1]); + + switch (ibuf[0]) { + case 0xaa: + debug_data_snipet(1, "INT Remote data snipet in", ibuf); + lme2510_remote_keypress(adap, + (u16)(ibuf[4]<<8)+ibuf[5]); + break; + case 0xbb: + switch (st->tuner_config) { + case TUNER_LG: + st->signal_lock = ibuf[2]; + st->signal_level = ibuf[4]; + st->signal_sn = ibuf[3]; + st->time_key = ibuf[7]; + break; + case TUNER_S7395: + /* Tweak for earlier firmware*/ + if (ibuf[1] == 0x03) { + st->signal_level = ibuf[3]; + st->signal_sn = ibuf[2]; + } else { + st->signal_level = ibuf[4]; + st->signal_sn = ibuf[5]; + } + break; + default: + break; + } + debug_data_snipet(5, "INT Remote data snipet in", ibuf); + break; + case 0xcc: + debug_data_snipet(1, "INT Control data snipet", ibuf); + break; + default: + debug_data_snipet(1, "INT Unknown data snipet", ibuf); + break; + } + } + usb_submit_urb(lme_urb, GFP_ATOMIC); +} + +static int lme2510_int_read(struct dvb_usb_adapter *adap) +{ + struct lme2510_state *lme_int = adap->dev->priv; + + lme_int->lme_urb = usb_alloc_urb(0, GFP_ATOMIC); + + if (lme_int->lme_urb == NULL) + return -ENOMEM; + + lme_int->buffer = usb_alloc_coherent(adap->dev->udev, 5000, GFP_ATOMIC, + &lme_int->lme_urb->transfer_dma); + + if (lme_int->buffer == NULL) + return -ENOMEM; + + usb_fill_int_urb(lme_int->lme_urb, + adap->dev->udev, + usb_rcvintpipe(adap->dev->udev, 0xa), + lme_int->buffer, + 4096, + lme2510_int_response, + adap, + 11); + + lme_int->lme_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_submit_urb(lme_int->lme_urb, GFP_ATOMIC); + info("INT Interupt Service Started"); + + return 0; +} + +static int lme2510_return_status(struct usb_device *dev) +{ + int ret = 0; + u8 data[10] = {0}; + ret |= usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x06, 0x80, 0x0302, 0x00, data, 0x0006, 200); + info("Firmware Status: %x (%x)", ret , data[2]); + + return (ret < 0) ? -ENODEV : data[2]; +} + +static int lme2510_msg(struct dvb_usb_device *d, + u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + int ret = 0; + struct lme2510_state *st = d->priv; + + if (st->i2c_talk_onoff == 1) { + if ((wbuf[2] == 0x1c) & (wbuf[3] == 0x0e)) + msleep(80); /*take your time when waiting for tune*/ + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); + + mutex_unlock(&d->i2c_mutex); + switch (st->tuner_config) { + case TUNER_S7395: + if (wbuf[3] == 0x24) + st->signal_lock = rbuf[1]; + break; + default: + break; + } + + } else { + switch (st->tuner_config) { + case TUNER_LG: + switch (wbuf[3]) { + case 0x0e: + rbuf[0] = 0x55; + rbuf[1] = st->signal_lock; + break; + case 0x43: + rbuf[0] = 0x55; + rbuf[1] = st->signal_level; + break; + case 0x1c: + rbuf[0] = 0x55; + rbuf[1] = st->signal_sn; + break; + default: + break; + } + break; + case TUNER_S7395: + switch (wbuf[3]) { + case 0x10: + rbuf[0] = 0x55; + rbuf[1] = (st->signal_level & 0x80) + ? 0 : (st->signal_level * 2); + break; + case 0x2d: + rbuf[0] = 0x55; + rbuf[1] = st->signal_sn; + break; + case 0x24: + rbuf[0] = 0x55; + rbuf[1] = (st->signal_level & 0x80) + ? 0 : st->signal_lock; + default: + break; + } + break; + default: + break; + + } + + deb_info(4, "I2C From Interupt Message out(%02x) in(%02x)", + wbuf[3], rbuf[1]); + + } + + return ret; +} + + +static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct lme2510_state *st = d->priv; + static u8 obuf[64], ibuf[512]; + int i, read, read_o; + u16 len; + u8 gate = st->i2c_gate; + + if (gate == 0) + gate = 5; + + if (num > 2) + warn("more than 2 i2c messages" + "at a time is not handled yet. TODO."); + + for (i = 0; i < num; i++) { + read_o = 1 & (msg[i].flags & I2C_M_RD); + read = i+1 < num && (msg[i+1].flags & I2C_M_RD); + read |= read_o; + gate = (msg[i].addr == st->i2c_tuner_addr) + ? (read) ? st->i2c_tuner_gate_r + : st->i2c_tuner_gate_w + : st->i2c_gate; + obuf[0] = gate | (read << 7); + + if (gate == 5) + obuf[1] = (read) ? 2 : msg[i].len + 1; + else + obuf[1] = msg[i].len + read + 1; + + obuf[2] = msg[i].addr; + if (read) { + if (read_o) + len = 3; + else { + memcpy(&obuf[3], msg[i].buf, msg[i].len); + obuf[msg[i].len+3] = msg[i+1].len; + len = msg[i].len+4; + } + } else { + memcpy(&obuf[3], msg[i].buf, msg[i].len); + len = msg[i].len+3; + } + + if (lme2510_msg(d, obuf, len, ibuf, 512) < 0) { + deb_info(1, "i2c transfer failed."); + return -EAGAIN; + } + + if (read) { + if (read_o) + memcpy(msg[i].buf, &ibuf[1], msg[i].len); + else { + memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len); + i++; + } + } + } + return i; +} + +static u32 lme2510_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm lme2510_i2c_algo = { + .master_xfer = lme2510_i2c_xfer, + .functionality = lme2510_i2c_func, +}; + +/* Callbacks for DVB USB */ +static int lme2510_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + if (lme2510_return_status(udev) == 0x44) + *cold = 1; + else + *cold = 0; + return 0; +} + +static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct lme2510_state *st = adap->dev->priv; + static u8 reset[] = LME_RESET; + static u8 stream_on[] = LME_ST_ON_W; + static u8 clear_reg_3[] = LME_CLEAR_PID; + static u8 rbuf[1]; + int ret = 0, len = 2, rlen = sizeof(rbuf); + + deb_info(1, "STM (%02x)", onoff); + + if (onoff == 1) { + st->i2c_talk_onoff = 0; + msleep(400); /* give enough time for i2c to stop */ + ret |= lme2510_usb_talk(adap->dev, + stream_on, len, rbuf, rlen); + } else { + deb_info(1, "STM Steam Off"); + if (st->tuner_config == TUNER_LG) + ret |= lme2510_usb_talk(adap->dev, clear_reg_3, + sizeof(clear_reg_3), rbuf, rlen); + else + ret |= lme2510_usb_talk(adap->dev, + reset, sizeof(reset), rbuf, rlen); + + msleep(400); + st->i2c_talk_onoff = 1; + } + + return (ret < 0) ? -ENODEV : 0; +} + +static int lme2510_int_service(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + struct input_dev *input_dev; + char *ir_codes = RC_MAP_LME2510; + int ret = 0; + + info("STA Configuring Remote"); + + usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys)); + + strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys)); + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + input_dev->name = "LME2510 Remote Control"; + input_dev->phys = d->rc_phys; + + usb_to_input_id(d->udev, &input_dev->id); + + ret |= ir_input_register(input_dev, ir_codes, NULL, "LME 2510"); + + if (ret) { + input_free_device(input_dev); + return ret; + } + + d->rc_input_dev = input_dev; + /* Start the Interupt */ + ret = lme2510_int_read(adap); + + if (ret < 0) { + ir_input_unregister(input_dev); + input_free_device(input_dev); + } + return (ret < 0) ? -ENODEV : 0; +} + +static u8 check_sum(u8 *p, u8 len) +{ + u8 sum = 0; + while (len--) + sum += *p++; + return sum; +} + +static int lme2510_download_firmware(struct usb_device *dev, + const struct firmware *fw) +{ + int ret = 0; + u8 data[512] = {0}; + u16 j, wlen, len_in, start, end; + u8 packet_size, dlen, i; + u8 *fw_data; + + packet_size = 0x31; + len_in = 1; + + + info("FRM Starting Firmware Download"); + + for (i = 1; i < 3; i++) { + start = (i == 1) ? 0 : 512; + end = (i == 1) ? 512 : fw->size; + for (j = start; j < end; j += (packet_size+1)) { + fw_data = (u8 *)(fw->data + j); + if ((end - j) > packet_size) { + data[0] = i; + dlen = packet_size; + } else { + data[0] = i | 0x80; + dlen = (u8)(end - j)-1; + } + data[1] = dlen; + memcpy(&data[2], fw_data, dlen+1); + wlen = (u8) dlen + 4; + data[wlen-1] = check_sum(fw_data, dlen+1); + deb_info(1, "Data S=%02x:E=%02x CS= %02x", data[3], + data[dlen+2], data[dlen+3]); + ret |= lme2510_bulk_write(dev, data, wlen, 1); + ret |= lme2510_bulk_read(dev, data, len_in , 1); + ret |= (data[0] == 0x88) ? 0 : -1; + } + } + usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x06, 0x80, 0x0200, 0x00, data, 0x0109, 1000); + + + data[0] = 0x8a; + len_in = 1; + msleep(2000); + ret |= lme2510_bulk_write(dev, data , len_in, 1); /*Resetting*/ + ret |= lme2510_bulk_read(dev, data, len_in, 1); + msleep(400); + + if (ret < 0) + info("FRM Firmware Download Failed (%04x)" , ret); + else + info("FRM Firmware Download Completed - Resetting Device"); + + + return (ret < 0) ? -ENODEV : 0; +} + + +static int lme2510_kill_urb(struct usb_data_stream *stream) +{ + int i; + for (i = 0; i < stream->urbs_submitted; i++) { + deb_info(3, "killing URB no. %d.", i); + + /* stop the URB */ + usb_kill_urb(stream->urb_list[i]); + } + stream->urbs_submitted = 0; + return 0; +} + +static struct tda10086_config tda10086_config = { + .demod_address = 0x1c, + .invert = 0, + .diseqc_tone = 1, + .xtal_freq = TDA10086_XTAL_16M, +}; + +static struct stv0288_config lme_config = { + .demod_address = 0xd0, + .min_delay_ms = 15, + .inittab = s7395_inittab, +}; + +static struct ix2505v_config lme_tuner = { + .tuner_address = 0xc0, + .min_delay_ms = 100, + .tuner_gain = 0x0, + .tuner_chargepump = 0x3, +}; + + +static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct lme2510_state *st = adap->dev->priv; + static u8 voltage_low[] = LME_VOLTAGE_L; + static u8 voltage_high[] = LME_VOLTAGE_H; + static u8 reset[] = LME_RESET; + static u8 clear_reg_3[] = LME_CLEAR_PID; + static u8 rbuf[1]; + int ret = 0, len = 3, rlen = 1; + + msleep(100); + + if (st->tuner_config == TUNER_LG) + ret |= lme2510_usb_talk(adap->dev, clear_reg_3, + sizeof(clear_reg_3), rbuf, rlen); + else + ret |= lme2510_usb_talk(adap->dev, + reset, sizeof(reset), rbuf, rlen); + + /*always check & stop streaming*/ + lme2510_kill_urb(&adap->stream); + adap->feedcount = 0; + + switch (voltage) { + + case SEC_VOLTAGE_18: + ret |= lme2510_usb_talk(adap->dev, + voltage_high, len, rbuf, rlen); + break; + + case SEC_VOLTAGE_OFF: + case SEC_VOLTAGE_13: + default: + ret |= lme2510_usb_talk(adap->dev, + voltage_low, len, rbuf, rlen); + break; + + + }; + st->i2c_talk_onoff = 1; + return (ret < 0) ? -ENODEV : 0; +} + +static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) +{ + int ret = 0; + struct lme2510_state *st = adap->dev->priv; + + /* Interupt Start */ + ret = lme2510_int_service(adap); + if (ret < 0) { + info("INT Unable to start Interupt Service"); + return -ENODEV; + } + + st->i2c_talk_onoff = 1; + st->i2c_gate = 4; + + adap->fe = dvb_attach(tda10086_attach, &tda10086_config, + &adap->dev->i2c_adap); + + if (adap->fe) { + info("TUN Found Frontend TDA10086"); + memcpy(&adap->fe->ops.info.name, + &"DM04_LG_TDQY-P001F DVB-S", 24); + adap->fe->ops.set_voltage = dm04_lme2510_set_voltage; + st->i2c_tuner_gate_w = 4; + st->i2c_tuner_gate_r = 4; + st->i2c_tuner_addr = 0xc0; + if (dvb_attach(tda826x_attach, adap->fe, 0xc0, + &adap->dev->i2c_adap, 1)) { + info("TUN TDA8263 Found"); + st->tuner_config = TUNER_LG; + return 0; + } + kfree(adap->fe); + adap->fe = NULL; + } + st->i2c_gate = 5; + adap->fe = dvb_attach(stv0288_attach, &lme_config, + &adap->dev->i2c_adap); + + if (adap->fe) { + info("FE Found Stv0288"); + memcpy(&adap->fe->ops.info.name, + &"DM04_SHARP:BS2F7HZ7395", 22); + adap->fe->ops.set_voltage = dm04_lme2510_set_voltage; + st->i2c_tuner_gate_w = 4; + st->i2c_tuner_gate_r = 5; + st->i2c_tuner_addr = 0xc0; + if (dvb_attach(ix2505v_attach , adap->fe, &lme_tuner, + &adap->dev->i2c_adap)) { + st->tuner_config = TUNER_S7395; + info("TUN Sharp IX2505V silicon tuner"); + return 0; + } + kfree(adap->fe); + adap->fe = NULL; + } + + info("DM04 Not Supported"); + return -ENODEV; +} + +static int lme2510_powerup(struct dvb_usb_device *d, int onoff) +{ + struct lme2510_state *st = d->priv; + st->i2c_talk_onoff = 1; + return 0; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties lme2510_properties; +static struct dvb_usb_device_properties lme2510c_properties; + +static int lme2510_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + int ret = 0; + + usb_reset_configuration(udev); + + usb_set_interface(udev, intf->cur_altsetting->desc.bInterfaceNumber, 1); + + if (udev->speed != USB_SPEED_HIGH) { + ret = usb_reset_device(udev); + info("DEV Failed to connect in HIGH SPEED mode"); + return -ENODEV; + } + + if (0 == dvb_usb_device_init(intf, &lme2510_properties, + THIS_MODULE, NULL, adapter_nr)) { + info("DEV registering device driver"); + return 0; + } + if (0 == dvb_usb_device_init(intf, &lme2510c_properties, + THIS_MODULE, NULL, adapter_nr)) { + info("DEV registering device driver"); + return 0; + } + + info("DEV lme2510 Error"); + return -ENODEV; + +} + +static struct usb_device_id lme2510_table[] = { + { USB_DEVICE(0x3344, 0x1122) }, /* LME2510 */ + { USB_DEVICE(0x3344, 0x1120) }, /* LME2510C */ + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, lme2510_table); + +static struct dvb_usb_device_properties lme2510_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = lme2510_download_firmware, + .firmware = "dvb-usb-lme2510-lg.fw", + + .size_of_priv = sizeof(struct lme2510_state), + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = lme2510_streaming_ctrl, + .frontend_attach = dm04_lme2510_frontend_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 10, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + + } + } + } + } + }, + .power_ctrl = lme2510_powerup, + .identify_state = lme2510_identify_state, + .i2c_algo = &lme2510_i2c_algo, + .generic_bulk_ctrl_endpoint = 0, + .num_device_descs = 1, + .devices = { + { "DM04 LME2510 DVB-S USB 2.0", + { &lme2510_table[0], NULL }, + }, + + } +}; + +static struct dvb_usb_device_properties lme2510c_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = lme2510_download_firmware, + .firmware = "dvb-usb-lme2510c-s7395.fw", + .size_of_priv = sizeof(struct lme2510_state), + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = lme2510_streaming_ctrl, + .frontend_attach = dm04_lme2510_frontend_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x8, + .u = { + .bulk = { + .buffersize = 4096, + + } + } + } + } + }, + .power_ctrl = lme2510_powerup, + .identify_state = lme2510_identify_state, + .i2c_algo = &lme2510_i2c_algo, + .generic_bulk_ctrl_endpoint = 0, + .num_device_descs = 1, + .devices = { + { "DM04 LME2510C USB2.0", + { &lme2510_table[1], NULL }, + }, + } +}; + +void lme2510_exit_int(struct dvb_usb_device *d) +{ + struct lme2510_state *st = d->priv; + if (st->lme_urb != NULL) { + st->i2c_talk_onoff = 0; + st->signal_lock = 0; + st->signal_level = 0; + st->signal_sn = 0; + kfree(st->usb_buffer); + usb_kill_urb(st->lme_urb); + usb_free_coherent(d->udev, 5000, st->buffer, + st->lme_urb->transfer_dma); + info("Interupt Service Stopped"); + ir_input_unregister(d->rc_input_dev); + info("Remote Stopped"); + } + return; +} + +void lme2510_exit(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + if (d != NULL) { + d->adapter[0].feedcount = 0; + lme2510_exit_int(d); + dvb_usb_device_exit(intf); + } + +} + +static struct usb_driver lme2510_driver = { + .name = "LME2510C_DVBS", + .probe = lme2510_probe, + .disconnect = lme2510_exit, + .id_table = lme2510_table, +}; + +/* module stuff */ +static int __init lme2510_module_init(void) +{ + int result = usb_register(&lme2510_driver); + if (result) { + err("usb_register failed. Error number %d", result); + return result; + } + + return 0; +} + +static void __exit lme2510_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&lme2510_driver); +} + +module_init(lme2510_module_init); +module_exit(lme2510_module_exit); + +MODULE_AUTHOR("Malcolm Priestley "); +MODULE_DESCRIPTION("LM2510(C) DVB-S USB2.0"); +MODULE_VERSION("1.4"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/lmedm04.h b/drivers/media/dvb/dvb-usb/lmedm04.h new file mode 100644 index 000000000000..5a66c7ec4511 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/lmedm04.h @@ -0,0 +1,187 @@ +/* DVB USB compliant linux driver for + * + * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395 + * LME2510C + LGTDQT-P001F + * + * MVB7395 (LME2510C+SHARP:BS2F7HZ7395) + * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V) + * + * MVB0001F (LME2510C+LGTDQT-P001F) + * LG TDQY - P001F =(TDA8263 + TDA10086H) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_LME2510_H_ +#define _DVB_USB_LME2510_H_ + +/* Streamer & PID + * + * Note: These commands do not actually stop the streaming + * but form some kind of packet filtering/stream count + * or tuning related functions. + * 06 XX + * offset 1 = 00 Enable Streaming + * + * + * PID + * 03 XX XX ----> reg number ---> setting....20 XX + * offset 1 = length + * offset 2 = start of data + * end byte -1 = 20 + * end byte = clear pid always a0, other wise 9c, 9a ?? + * + * RESET (also clears PID filter) + * 3a 01 00 +*/ +#define LME_ST_ON_W {0x06, 0x00} +#define LME_RESET {0x3a, 0x01, 0x00} +#define LME_CLEAR_PID {0x03, 0x02, 0x20, 0xa0} + + +/* LME Power Control + * 07 XX XX + * offset 1 = 01 Power? my device cannot be powered down + * offset 2 = 00=Voltage low 01=Voltage high + */ + +#define LME_VOLTAGE_L {0x07, 0x01, 0x00} +#define LME_VOLTAGE_H {0x07, 0x01, 0x01} + + +/* Initial stv0288 settings for 7395 Frontend */ +static u8 s7395_inittab[] = { + 0x00, 0x11, + 0x01, 0x15, + 0x02, 0x20, + 0x03, 0x8e, + 0x04, 0x8e, + 0x05, 0x12, + 0x06, 0xff, + 0x07, 0x20, + 0x08, 0x00, + 0x09, 0x00, + 0x0a, 0x04, + 0x0b, 0x00, + 0x0c, 0x00, + 0x0d, 0x00, + 0x0e, 0xc1, + 0x0f, 0x54, + 0x10, 0x40, + 0x11, 0x7a, + 0x12, 0x03, + 0x13, 0x48, + 0x14, 0x84, + 0x15, 0xc5, + 0x16, 0xb8, + 0x17, 0x9c, + 0x18, 0x00, + 0x19, 0xa6, + 0x1a, 0x88, + 0x1b, 0x8f, + 0x1c, 0xf0, + 0x1e, 0x80, + 0x20, 0x0b, + 0x21, 0x54, + 0x22, 0xff, + 0x23, 0x01, + 0x24, 0x9a, + 0x25, 0x7f, + 0x26, 0x00, + 0x27, 0x00, + 0x28, 0x46, + 0x29, 0x66, + 0x2a, 0x90, + 0x2b, 0xfa, + 0x2c, 0xd9, + 0x2d, 0x02, + 0x2e, 0xb1, + 0x2f, 0x00, + 0x30, 0x0, + 0x31, 0x1e, + 0x32, 0x14, + 0x33, 0x0f, + 0x34, 0x09, + 0x35, 0x0c, + 0x36, 0x05, + 0x37, 0x2f, + 0x38, 0x16, + 0x39, 0xbd, + 0x3a, 0x0, + 0x3b, 0x13, + 0x3c, 0x11, + 0x3d, 0x30, + 0x3e, 0x00, + 0x3f, 0x00, + 0x40, 0x63, + 0x41, 0x04, + 0x42, 0x60, + 0x43, 0x00, + 0x44, 0x00, + 0x45, 0x00, + 0x46, 0x00, + 0x47, 0x00, + 0x4a, 0x00, + 0x4b, 0xd1, + 0x4c, 0x33, + 0x50, 0x12, + 0x51, 0x36, + 0x52, 0x21, + 0x53, 0x94, + 0x54, 0xb2, + 0x55, 0x29, + 0x56, 0x64, + 0x57, 0x2b, + 0x58, 0x54, + 0x59, 0x86, + 0x5a, 0x00, + 0x5b, 0x9b, + 0x5c, 0x08, + 0x5d, 0x7f, + 0x5e, 0xff, + 0x5f, 0x8d, + 0x70, 0x0, + 0x71, 0x0, + 0x72, 0x0, + 0x74, 0x0, + 0x75, 0x0, + 0x76, 0x0, + 0x81, 0x0, + 0x82, 0x3f, + 0x83, 0x3f, + 0x84, 0x0, + 0x85, 0x0, + 0x88, 0x0, + 0x89, 0x0, + 0x8a, 0x0, + 0x8b, 0x0, + 0x8c, 0x0, + 0x90, 0x0, + 0x91, 0x0, + 0x92, 0x0, + 0x93, 0x0, + 0x94, 0x1c, + 0x97, 0x0, + 0xa0, 0x48, + 0xa1, 0x0, + 0xb0, 0xb8, + 0xb1, 0x3a, + 0xb2, 0x10, + 0xb3, 0x82, + 0xb4, 0x80, + 0xb5, 0x82, + 0xb6, 0x82, + 0xb7, 0x82, + 0xb8, 0x20, + 0xb9, 0x0, + 0xf0, 0x0, + 0xf1, 0x0, + 0xf2, 0xc0, + 0xff, 0xff, +}; + +#endif diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 6c0324eb4914..57281b1008e4 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -96,6 +96,7 @@ void rc_map_init(void); #define RC_MAP_KWORLD_315U "rc-kworld-315u" #define RC_MAP_KWORLD_PLUS_TV_ANALOG "rc-kworld-plus-tv-analog" #define RC_MAP_LIRC "rc-lirc" +#define RC_MAP_LME2510 "rc-lme2510" #define RC_MAP_MANLI "rc-manli" #define RC_MAP_MSI_TVANYWHERE_PLUS "rc-msi-tvanywhere-plus" #define RC_MAP_MSI_TVANYWHERE "rc-msi-tvanywhere" -- cgit v1.2.3-70-g09d2 From f44026dbe42d38f2e2dfdc7bf35cafc7e44794d7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 6 Aug 2010 12:52:43 -0300 Subject: V4L/DVB: Documentation: update now that the vtx/videotext API has been removed Remove all references to /dev/vtx in the documentation, except for some historical comments in dev-teletext.xml. Documentation/devices.txt is not updated, this will go through Alan Cox who maintains this file. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/compat.xml | 24 +++++++++++++++-------- Documentation/DocBook/v4l/dev-teletext.xml | 29 +++++++++++++--------------- Documentation/DocBook/v4l/v4l2.xml | 10 +++++++++- Documentation/feature-removal-schedule.txt | 23 ---------------------- Documentation/ioctl/ioctl-number.txt | 1 - Documentation/video4linux/bttv/MAKEDEV | 1 - Documentation/video4linux/v4l2-framework.txt | 5 ++--- 7 files changed, 40 insertions(+), 53 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/compat.xml b/Documentation/DocBook/v4l/compat.xml index 54447f0d0784..c9ce61d981f5 100644 --- a/Documentation/DocBook/v4l/compat.xml +++ b/Documentation/DocBook/v4l/compat.xml @@ -21,11 +21,15 @@ API. Opening and Closing Devices For compatibility reasons the character device file names -recommended for V4L2 video capture, overlay, radio, teletext and raw +recommended for V4L2 video capture, overlay, radio and raw vbi capture devices did not change from those used by V4L. They are listed in and below in . + The teletext devices (minor range 192-223) have been removed in +V4L2 and no longer exist. There is no hardware available anymore for handling +pure teletext. Instead raw or sliced VBI is used. + The V4L videodev module automatically assigns minor numbers to drivers in load order, depending on the registered device type. We recommend that V4L2 drivers by default @@ -65,13 +69,6 @@ not compatible with V4L or V4L2. , /dev/radio63 64-127 - - Teletext decoder - /dev/vtx, -/dev/vtx0 to -/dev/vtx31 - 192-223 - Raw VBI capture /dev/vbi, @@ -2345,6 +2342,17 @@ more information. +
+ V4L2 in Linux 2.6.37 + + + Remove the vtx (videotext/teletext) API. This API was no longer +used and no hardware exists to verify the API. Nor were any userspace applications found +that used it. It was originally scheduled for removal in 2.6.35. + + + +
Relation of V4L2 to other Linux multimedia APIs diff --git a/Documentation/DocBook/v4l/dev-teletext.xml b/Documentation/DocBook/v4l/dev-teletext.xml index 76184e8ed618..414b1cfff9f4 100644 --- a/Documentation/DocBook/v4l/dev-teletext.xml +++ b/Documentation/DocBook/v4l/dev-teletext.xml @@ -1,35 +1,32 @@ Teletext Interface - This interface aims at devices receiving and demodulating + This interface was aimed at devices receiving and demodulating Teletext data [, ], evaluating the Teletext packages and storing formatted pages in cache memory. Such devices are usually implemented as microcontrollers with serial -interface (I2C) and can be found on older +interface (I2C) and could be found on old TV cards, dedicated Teletext decoding cards and home-brew devices connected to the PC parallel port. - The Teletext API was designed by Martin Buck. It is defined in + The Teletext API was designed by Martin Buck. It was defined in the kernel header file linux/videotext.h, the specification is available from ftp://ftp.gwdg.de/pub/linux/misc/videotext/. (Videotext is the name of -the German public television Teletext service.) Conventional character -device file names are /dev/vtx and -/dev/vttuner, with device number 83, 0 and 83, 16 -respectively. A similar interface exists for the Philips SAA5249 -Teletext decoder [specification?] with character device file names -/dev/tlkN, device number 102, N. +the German public television Teletext service.) Eventually the Teletext API was integrated into the V4L API with character device file names /dev/vtx0 to /dev/vtx31, device major number 81, minor numbers -192 to 223. For reference the V4L Teletext API specification is -reproduced here in full: "Teletext interfaces talk the existing VTX -API." Teletext devices with major number 83 and 102 will be removed in -Linux 2.6. +192 to 223. - There are no plans to replace the Teletext API or to integrate -it into V4L2. Please write to the linux-media mailing list: &v4l-ml; -when the need arises. + However, teletext decoders were quickly replaced by more +generic VBI demodulators and those dedicated teletext decoders no longer exist. +For many years the vtx devices were still around, even though nobody used +them. So the decision was made to finally remove support for the Teletext API in +kernel 2.6.37. + + Modern devices all use the raw or +sliced VBI API. + + 2.6.37 + 2010-08-06 + hv + Removed obsolete vtx (videotext) API. + + 2.6.33 2009-12-03 diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 842aa9de84a6..2372fb221969 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -498,29 +498,6 @@ When: April 2011 Why: Superseded by xt_CT Who: Netfilter developer team ---------------------------- - -What: video4linux /dev/vtx teletext API support -When: 2.6.35 -Files: drivers/media/video/saa5246a.c drivers/media/video/saa5249.c - include/linux/videotext.h -Why: The vtx device nodes have been superseded by vbi device nodes - for many years. No applications exist that use the vtx support. - Of the two i2c drivers that actually support this API the saa5249 - has been impossible to use for a year now and no known hardware - that supports this device exists. The saa5246a is theoretically - supported by the old mxb boards, but it never actually worked. - - In summary: there is no hardware that can use this API and there - are no applications actually implementing this API. - - The vtx support still reserves minors 192-223 and we would really - like to reuse those for upcoming new functionality. In the unlikely - event that new hardware appears that wants to use the functionality - provided by the vtx API, then that functionality should be build - around the sliced VBI API instead. -Who: Hans Verkuil - ---------------------------- What: IRQF_DISABLED diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 33223ff121d8..10f5af8b73f7 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -278,7 +278,6 @@ Code Seq#(hex) Include File Comments 'z' 10-4F drivers/s390/crypto/zcrypt_api.h conflict! 0x80 00-1F linux/fb.h -0x81 00-1F linux/videotext.h 0x88 00-3F media/ovcamchip.h 0x89 00-06 arch/x86/include/asm/sockios.h 0x89 0B-DF linux/sockios.h diff --git a/Documentation/video4linux/bttv/MAKEDEV b/Documentation/video4linux/bttv/MAKEDEV index 9d112f7fd5f7..093c0cd18042 100644 --- a/Documentation/video4linux/bttv/MAKEDEV +++ b/Documentation/video4linux/bttv/MAKEDEV @@ -19,7 +19,6 @@ function makedev () { echo "*** new device names ***" makedev video 0 makedev radio 64 -makedev vtx 192 makedev vbi 224 #echo "*** old device names (for compatibility only) ***" diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index f5fdb395287f..8fb9de4f4875 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -44,8 +44,8 @@ All drivers have the following structure: 2) A way of initializing and commanding sub-devices (if any). -3) Creating V4L2 device nodes (/dev/videoX, /dev/vbiX, /dev/radioX and - /dev/vtxX) and keeping track of device-node specific data. +3) Creating V4L2 device nodes (/dev/videoX, /dev/vbiX and /dev/radioX) + and keeping track of device-node specific data. 4) Filehandle-specific structs containing per-filehandle data; @@ -488,7 +488,6 @@ types exist: VFL_TYPE_GRABBER: videoX for video input/output devices VFL_TYPE_VBI: vbiX for vertical blank data (i.e. closed captions, teletext) VFL_TYPE_RADIO: radioX for radio tuners -VFL_TYPE_VTX: vtxX for teletext devices (deprecated, don't use) The last argument gives you a certain amount of control over the device device node number used (i.e. the X in videoX). Normally you will pass -1 -- cgit v1.2.3-70-g09d2 From 87660547def9d343dd5d21644b7a79a15a9b83c1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 4 Sep 2010 00:53:30 -0300 Subject: devices.txt: Remove the old obsolete vtx device nodes Teletext interface is provided via VBI interface for a long time. There's no need nor is there any known program using those old /dev/vtx nodes. Signed-off-by: Mauro Carvalho Chehab --- Documentation/devices.txt | 3 --- 1 file changed, 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devices.txt b/Documentation/devices.txt index d0d1df6cb5de..76b1d2f22b6a 100644 --- a/Documentation/devices.txt +++ b/Documentation/devices.txt @@ -1495,9 +1495,6 @@ Your cooperation is appreciated. 64 = /dev/radio0 Radio device ... 127 = /dev/radio63 Radio device - 192 = /dev/vtx0 Teletext device - ... - 223 = /dev/vtx31 Teletext device 224 = /dev/vbi0 Vertical blank interrupt ... 255 = /dev/vbi31 Vertical blank interrupt -- cgit v1.2.3-70-g09d2 From 860e7f474cb5bd9a691ebde302ac094093a1f5a7 Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Mon, 13 Sep 2010 06:40:17 -0300 Subject: V4L/DVB: gspca - sonixj: Webcam 0c45:6102 added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Simon Danner Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 1 + drivers/media/video/gspca/sonixj.c | 2 +- drivers/media/video/sn9c102/sn9c102_devtable.h | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 56ba7bba7168..3a2f40779fba 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -302,6 +302,7 @@ sonixj 0c45:60fb Surfer NoName sonixj 0c45:60fc LG-LIC300 sonixj 0c45:60fe Microdia Audio sonixj 0c45:6100 PC Camera (SN9C128) +sonixj 0c45:6102 PC Camera (SN9C128) sonixj 0c45:610a PC Camera (SN9C128) sonixj 0c45:610b PC Camera (SN9C128) sonixj 0c45:610c PC Camera (SN9C128) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 6e410121df25..89e488cb9bb8 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -3019,7 +3019,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)}, #endif {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)}, /*sn9c128*/ -/* {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, PO2030N)}, * / GC0305*/ + {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, PO2030N)}, /* /GC0305*/ /* {USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */ {USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)}, /*sn9c128*/ {USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)}, /*sn9c128*/ diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index b6643ca7656a..62ee1f035d6b 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -116,7 +116,9 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, /* SN9C120 */ { SN9C102_USB_DEVICE(0x0458, 0x7025, BRIDGE_SN9C120), }, +#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, -- cgit v1.2.3-70-g09d2 From 2a3b501ffd28b2be355f558479543028ccdead48 Mon Sep 17 00:00:00 2001 From: Alexander Goncharov Date: Mon, 13 Sep 2010 06:58:16 -0300 Subject: V4L/DVB: gspca - sonixj: Add webcam 0c45:612b MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexander Goncharov Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 1 + drivers/media/video/gspca/sonixj.c | 1 + 2 files changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 3a2f40779fba..6a562eeeb4cd 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -309,6 +309,7 @@ sonixj 0c45:610c PC Camera (SN9C128) sonixj 0c45:610e PC Camera (SN9C128) sonixj 0c45:6128 Microdia/Sonix SNP325 sonixj 0c45:612a Avant Camera +sonixj 0c45:612b Speed-Link REFLECT2 sonixj 0c45:612c Typhoon Rasy Cam 1.3MPix sonixj 0c45:6130 Sonix Pccam sonixj 0c45:6138 Sn9c120 Mo4000 diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 89e488cb9bb8..025f9ba5c681 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -3031,6 +3031,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)}, /*sn9c325?*/ /*bw600.inf:*/ {USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)}, /*sn9c325?*/ + {USB_DEVICE(0x0c45, 0x612b), BS(SN9C110, ADCM1700)}, {USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)}, {USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)}, /* {USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */ -- cgit v1.2.3-70-g09d2 From 008d35f2f5256751a18f1f4aea79e3caf140098d Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Mon, 13 Sep 2010 07:04:49 -0300 Subject: V4L/DVB: v4l2: Add illuminator controls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some media devices (microscopes) may have one or many illuminators. This patch makes them controlable by the applications. Acked-by: Hans Verkuil Acked-by: Andy Walls Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/controls.xml | 9 ++++++++- drivers/media/video/v4l2-ctrls.c | 4 ++++ include/linux/videodev2.h | 5 ++++- 3 files changed, 16 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/controls.xml b/Documentation/DocBook/v4l/controls.xml index 8408caaee276..68c83449d7c9 100644 --- a/Documentation/DocBook/v4l/controls.xml +++ b/Documentation/DocBook/v4l/controls.xml @@ -311,11 +311,18 @@ minimum value disables backlight compensation. bits 8-15 Green color information, bits 16-23 Blue color information and bits 24-31 must be zero. + + V4L2_CID_ILLUMINATORS_1 + V4L2_CID_ILLUMINATORS_2 + boolean + Switch on or off the illuminator 1 or 2 of the device + (usually a microscope). + V4L2_CID_LASTP1 End of the predefined control IDs (currently -V4L2_CID_BG_COLOR + 1). +V4L2_CID_ILLUMINATORS_2 + 1). V4L2_CID_PRIVATE_BASE diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index ea8d32cd425d..9d2502cd03ff 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -305,6 +305,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_ROTATE: return "Rotate"; case V4L2_CID_BG_COLOR: return "Background Color"; case V4L2_CID_CHROMA_GAIN: return "Chroma Gain"; + case V4L2_CID_ILLUMINATORS_1: return "Illuminator 1"; + case V4L2_CID_ILLUMINATORS_2: return "Illuminator 2"; /* MPEG controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -419,6 +421,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_AUDIO_LIMITER_ENABLED: case V4L2_CID_AUDIO_COMPRESSION_ENABLED: case V4L2_CID_PILOT_TONE_ENABLED: + case V4L2_CID_ILLUMINATORS_1: + case V4L2_CID_ILLUMINATORS_2: *type = V4L2_CTRL_TYPE_BOOLEAN; *min = 0; *max = *step = 1; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 2d5ce17ca472..b06479f63271 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1047,8 +1047,11 @@ enum v4l2_colorfx { #define V4L2_CID_CHROMA_GAIN (V4L2_CID_BASE+36) +#define V4L2_CID_ILLUMINATORS_1 (V4L2_CID_BASE+37) +#define V4L2_CID_ILLUMINATORS_2 (V4L2_CID_BASE+38) + /* last CID + 1 */ -#define V4L2_CID_LASTP1 (V4L2_CID_BASE+37) +#define V4L2_CID_LASTP1 (V4L2_CID_BASE+39) /* MPEG-class control IDs defined by V4L2 */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) -- cgit v1.2.3-70-g09d2 From 5e765c6e445da16527a04aa3931f21e5deb476d8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 11 Sep 2010 08:36:55 -0300 Subject: V4L/DVB: V4L Doc: fix DocBook syntax errors Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/pixfmt.xml | 4 ++-- Documentation/DocBook/v4l/vidioc-g-dv-preset.xml | 3 +-- Documentation/DocBook/v4l/vidioc-g-dv-timings.xml | 3 +-- Documentation/DocBook/v4l/vidioc-query-dv-preset.xml | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml index 5376aefeeffc..f4cc0a9e658e 100644 --- a/Documentation/DocBook/v4l/pixfmt.xml +++ b/Documentation/DocBook/v4l/pixfmt.xml @@ -787,14 +787,14 @@ http://www.thedirks.org/winnov/ 'TM60' Used by Trident tm6000 - + V4L2_PIX_FMT_CIT_YYVYUY 'CITV' Used by xirlink CIT, found at IBM webcams. Uses one line of Y then 1 line of VYUY - + V4L2_PIX_FMT_KONICA420 'KONI' Used by Konica webcams. diff --git a/Documentation/DocBook/v4l/vidioc-g-dv-preset.xml b/Documentation/DocBook/v4l/vidioc-g-dv-preset.xml index 3c6784e132f3..d733721a7519 100644 --- a/Documentation/DocBook/v4l/vidioc-g-dv-preset.xml +++ b/Documentation/DocBook/v4l/vidioc-g-dv-preset.xml @@ -16,8 +16,7 @@ int ioctl int fd int request - &v4l2-dv-preset; -*argp + struct v4l2_dv_preset *argp diff --git a/Documentation/DocBook/v4l/vidioc-g-dv-timings.xml b/Documentation/DocBook/v4l/vidioc-g-dv-timings.xml index ecc19576bb8f..d5ec6abf0ce2 100644 --- a/Documentation/DocBook/v4l/vidioc-g-dv-timings.xml +++ b/Documentation/DocBook/v4l/vidioc-g-dv-timings.xml @@ -16,8 +16,7 @@ int ioctl int fd int request - &v4l2-dv-timings; -*argp + struct v4l2_dv_timings *argp diff --git a/Documentation/DocBook/v4l/vidioc-query-dv-preset.xml b/Documentation/DocBook/v4l/vidioc-query-dv-preset.xml index 402229ee06f6..d272f7ab91b8 100644 --- a/Documentation/DocBook/v4l/vidioc-query-dv-preset.xml +++ b/Documentation/DocBook/v4l/vidioc-query-dv-preset.xml @@ -16,7 +16,7 @@ input int ioctl int fd int request - &v4l2-dv-preset; *argp + struct v4l2_dv_preset *argp -- cgit v1.2.3-70-g09d2 From 0827d0664dfa046dc9359c1a45f613d916431ed7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 11 Sep 2010 08:39:47 -0300 Subject: V4L/DVB: V4L Doc: document V4L2_CAP_RDS_OUTPUT capability We forgot to add this capability to the QUERYCAP documentation. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/vidioc-querycap.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/vidioc-querycap.xml b/Documentation/DocBook/v4l/vidioc-querycap.xml index 6ab7e25b31b6..d499da93a450 100644 --- a/Documentation/DocBook/v4l/vidioc-querycap.xml +++ b/Documentation/DocBook/v4l/vidioc-querycap.xml @@ -184,7 +184,7 @@ data. V4L2_CAP_RDS_CAPTURE 0x00000100 - The device supports the RDS interface. + The device supports the RDS capture interface. V4L2_CAP_VIDEO_OUTPUT_OVERLAY @@ -205,6 +205,11 @@ driver capabilities. The device supports the &VIDIOC-S-HW-FREQ-SEEK; ioctl for hardware frequency seeking. + + V4L2_CAP_RDS_OUTPUT + 0x00000800 + The device supports the RDS output interface. + V4L2_CAP_TUNER 0x00010000 -- cgit v1.2.3-70-g09d2 From 6756207a71a0f976ab027186d0966a2162aaf922 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 11 Sep 2010 11:38:51 -0300 Subject: V4L/DVB: V4L Doc: correct the documentation for VIDIOC_QUERYMENU The VIDIOC_QUERYMENU documentation was not correct. EINVAL can be returned if the driver does not support some of the menu items. I.e. in a list of MPEG bitrates a driver generally supports only a subset of these. This behavior has been in place for years, but was never properly documented. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/controls.xml | 3 --- Documentation/DocBook/v4l/vidioc-queryctrl.xml | 18 ++++++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/controls.xml b/Documentation/DocBook/v4l/controls.xml index 68c83449d7c9..2fae3e87ce73 100644 --- a/Documentation/DocBook/v4l/controls.xml +++ b/Documentation/DocBook/v4l/controls.xml @@ -364,9 +364,6 @@ enumerate_menu (void) querymenu.index++) { if (0 == ioctl (fd, &VIDIOC-QUERYMENU;, &querymenu)) { printf (" %s\n", querymenu.name); - } else { - perror ("VIDIOC_QUERYMENU"); - exit (EXIT_FAILURE); } } } diff --git a/Documentation/DocBook/v4l/vidioc-queryctrl.xml b/Documentation/DocBook/v4l/vidioc-queryctrl.xml index 8e0e055ac934..0d5e8283cf32 100644 --- a/Documentation/DocBook/v4l/vidioc-queryctrl.xml +++ b/Documentation/DocBook/v4l/vidioc-queryctrl.xml @@ -103,8 +103,12 @@ structure. The driver fills the rest of the structure or returns an index is invalid. Menu items are enumerated by calling VIDIOC_QUERYMENU with successive index values from &v4l2-queryctrl; -minimum (0) to -maximum, inclusive. +minimum to +maximum, inclusive. Note that it is possible +for VIDIOC_QUERYMENU to return an &EINVAL; for some +indices between minimum and maximum. +In that case that particular menu item is not supported by this driver. Also note that +the minimum value is not necessarily 0. See also the examples in . @@ -139,7 +143,7 @@ string. This information is intended for the user. minimum Minimum value, inclusive. This field gives a lower bound for V4L2_CTRL_TYPE_INTEGER controls and the -lowest valid index (always 0) for V4L2_CTRL_TYPE_MENU controls. +lowest valid index for V4L2_CTRL_TYPE_MENU controls. For V4L2_CTRL_TYPE_STRING controls the minimum value gives the minimum length of the string. This length does not include the terminating zero. It may not be valid for any other type of control, including @@ -279,7 +283,7 @@ values which are actually different on the hardware. V4L2_CTRL_TYPE_MENU - 0 + ≥ 0 1 N-1 The control has a menu of N choices. The names of @@ -405,8 +409,10 @@ writing a value will cause the device to carry out a given action EINVAL The &v4l2-queryctrl; id -is invalid. The &v4l2-querymenu; id or -index is invalid. +is invalid. The &v4l2-querymenu; id is +invalid or index is out of range (less than +minimum or greater than maximum) +or this particular menu item is not supported by the driver. -- cgit v1.2.3-70-g09d2 From a1de2e4b72f27b906cb2eea3003fd62377dbf5ea Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 11 Sep 2010 07:05:47 -0300 Subject: V4L/DVB: V4L Doc: removed duplicate link Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/pixfmt-packed-rgb.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/pixfmt-packed-rgb.xml b/Documentation/DocBook/v4l/pixfmt-packed-rgb.xml index 26e879231088..4db272b8a0d3 100644 --- a/Documentation/DocBook/v4l/pixfmt-packed-rgb.xml +++ b/Documentation/DocBook/v4l/pixfmt-packed-rgb.xml @@ -739,7 +739,7 @@ defined in error. Drivers may interpret them as in b1 b0 - + V4L2_PIX_FMT_BGR666 'BGRH' -- cgit v1.2.3-70-g09d2 From 7af97effb3f5a374482179aca490b5038de56fa6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 30 Sep 2010 08:25:42 -0300 Subject: V4L/DVB: Deprecate cpia driver (used for parallel port webcams) cpia driver were re-written inside gspca driver, for USB devices. The only functionality that were not migrated is the support for parallel port, as: 1) the developer didn't find any hardware; 2) it doesn't seem important to keep support for a parallel port webcam, as this is an obsolete technology; 3) the changes at gspca for it to work with parallel port would be very large; 4) this driver still uses BKL. So, let's move it to drivers/staging and label it to die at 2.6.38, if nobody cares enough to port parallel port support to gspca or to create a new driver that uses the same gspca-cpia sub-driver. Signed-off-by: Mauro Carvalho Chehab --- Documentation/feature-removal-schedule.txt | 17 +- drivers/media/video/Kconfig | 40 - drivers/media/video/Makefile | 3 - drivers/media/video/cpia.c | 4032 ---------------------------- drivers/media/video/cpia.h | 432 --- drivers/media/video/cpia_pp.c | 869 ------ drivers/media/video/cpia_usb.c | 640 ----- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/cpia/Kconfig | 39 + drivers/staging/cpia/Makefile | 5 + drivers/staging/cpia/TODO | 8 + drivers/staging/cpia/cpia.c | 4032 ++++++++++++++++++++++++++++ drivers/staging/cpia/cpia.h | 432 +++ drivers/staging/cpia/cpia_pp.c | 869 ++++++ drivers/staging/cpia/cpia_usb.c | 640 +++++ 16 files changed, 6044 insertions(+), 6017 deletions(-) delete mode 100644 drivers/media/video/cpia.c delete mode 100644 drivers/media/video/cpia.h delete mode 100644 drivers/media/video/cpia_pp.c delete mode 100644 drivers/media/video/cpia_usb.c create mode 100644 drivers/staging/cpia/Kconfig create mode 100644 drivers/staging/cpia/Makefile create mode 100644 drivers/staging/cpia/TODO create mode 100644 drivers/staging/cpia/cpia.c create mode 100644 drivers/staging/cpia/cpia.h create mode 100644 drivers/staging/cpia/cpia_pp.c create mode 100644 drivers/staging/cpia/cpia_usb.c (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 2372fb221969..448722555648 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -98,7 +98,7 @@ Who: Pavel Machek --------------------------- What: Video4Linux API 1 ioctls and from Video devices. -When: July 2009 +When: kernel 2.6.38 Files: include/linux/videodev.h Check: include/linux/videodev.h Why: V4L1 AP1 was replaced by V4L2 API during migration from 2.4 to 2.6 @@ -116,6 +116,21 @@ Who: Mauro Carvalho Chehab --------------------------- +What: Video4Linux obsolete drivers using V4L1 API +When: kernel 2.6.38 +Files: drivers/staging/cpia/* +Check: drivers/staging/cpia/cpia.c +Why: There are some drivers still using V4L1 API, despite all efforts we've done + to migrate. Those drivers are for obsolete hardware that the old maintainer + didn't care (or not have the hardware anymore), and that no other developer + could find any hardware to buy. They probably have no practical usage today, + and people with such old hardware could probably keep using an older version + of the kernel. Those drivers will be moved to staging on 2.6.37 and, if nobody + care enough to port and test them with V4L2 API, they'll be removed on 2.6.38. +Who: Mauro Carvalho Chehab + +--------------------------- + What: sys_sysctl When: September 2010 Option: CONFIG_SYSCTL_SYSCALL diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 2c0a8f776f8a..4f21ef877f37 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -599,46 +599,6 @@ config VIDEO_W9966 Check out for more information. -config VIDEO_CPIA - tristate "CPiA Video For Linux (DEPRECATED)" - depends on VIDEO_V4L1 - default n - ---help--- - This driver is DEPRECATED please use the gspca cpia1 module - instead. Note that you need atleast version 0.6.4 of libv4l for - the cpia1 gspca module. - - This is the video4linux driver for cameras based on Vision's CPiA - (Colour Processor Interface ASIC), such as the Creative Labs Video - Blaster Webcam II. If you have one of these cameras, say Y here - and select parallel port and/or USB lowlevel support below, - otherwise say N. This will not work with the Creative Webcam III. - - Please read for more - information. - - This driver is also available as a module (cpia). - -config VIDEO_CPIA_PP - tristate "CPiA Parallel Port Lowlevel Support" - depends on PARPORT_1284 && VIDEO_CPIA && PARPORT - help - This is the lowlevel parallel port support for cameras based on - Vision's CPiA (Colour Processor Interface ASIC), such as the - Creative Webcam II. If you have the parallel port version of one - of these cameras, say Y here, otherwise say N. It is also available - as a module (cpia_pp). - -config VIDEO_CPIA_USB - tristate "CPiA USB Lowlevel Support" - depends on VIDEO_CPIA && USB - help - This is the lowlevel USB support for cameras based on Vision's CPiA - (Colour Processor Interface ASIC), such as the Creative Webcam II. - If you have the USB version of one of these cameras, say Y here, - otherwise say N. This will not work with the Creative Webcam III. - It is also available as a module (cpia_usb). - source "drivers/media/video/cpia2/Kconfig" config VIDEO_VINO diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index a8f89899dd6b..b947160a8cb0 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -92,9 +92,6 @@ obj-$(CONFIG_VIDEO_W9966) += w9966.o obj-$(CONFIG_VIDEO_PMS) += pms.o obj-$(CONFIG_VIDEO_VINO) += vino.o obj-$(CONFIG_VIDEO_STRADIS) += stradis.o -obj-$(CONFIG_VIDEO_CPIA) += cpia.o -obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o -obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c deleted file mode 100644 index 933ae4c8cb9a..000000000000 --- a/drivers/media/video/cpia.c +++ /dev/null @@ -1,4032 +0,0 @@ -/* - * cpia CPiA driver - * - * Supports CPiA based Video Camera's. - * - * (C) Copyright 1999-2000 Peter Pregler - * (C) Copyright 1999-2000 Scott J. Bertin - * (C) Copyright 1999-2000 Johannes Erdfelt - * (C) Copyright 2000 STMicroelectronics - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ -/* #define _CPIA_DEBUG_ 1 */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cpia.h" - -static int video_nr = -1; - -#ifdef MODULE -module_param(video_nr, int, 0); -MODULE_AUTHOR("Scott J. Bertin & Peter Pregler & Johannes Erdfelt "); -MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("video"); -#endif - -static unsigned short colorspace_conv; -module_param(colorspace_conv, ushort, 0444); -MODULE_PARM_DESC(colorspace_conv, - " Colorspace conversion:" - "\n 0 = disable, 1 = enable" - "\n Default value is 0" - ); - -#define ABOUT "V4L-Driver for Vision CPiA based cameras" - -#define CPIA_MODULE_CPIA (0<<5) -#define CPIA_MODULE_SYSTEM (1<<5) -#define CPIA_MODULE_VP_CTRL (5<<5) -#define CPIA_MODULE_CAPTURE (6<<5) -#define CPIA_MODULE_DEBUG (7<<5) - -#define INPUT (DATA_IN << 8) -#define OUTPUT (DATA_OUT << 8) - -#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1) -#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2) -#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3) -#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4) -#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5) -#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7) -#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8) -#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10) - -#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1) -#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2) -#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3) -#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4) -#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5) -#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6) -#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7) -#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8) -#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9) -#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10) -#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11) -#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12) -#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13) - -#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1) -#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2) -#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3) -#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4) -#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6) -#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7) -#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8) -#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9) -#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10) -#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11) -#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16) -#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17) -#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18) -#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19) -#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25) -#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30) -#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31) - -#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1) -#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2) -#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3) -#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4) -#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5) -#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6) -#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7) -#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8) -#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9) -#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10) -#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11) -#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12) -#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13) -#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14) -#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15) - -#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1) -#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4) -#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5) -#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6) -#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8) -#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9) -#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10) -#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11) - -enum { - FRAME_READY, /* Ready to grab into */ - FRAME_GRABBING, /* In the process of being grabbed into */ - FRAME_DONE, /* Finished grabbing, but not been synced yet */ - FRAME_UNUSED, /* Unused (no MCAPTURE) */ -}; - -#define COMMAND_NONE 0x0000 -#define COMMAND_SETCOMPRESSION 0x0001 -#define COMMAND_SETCOMPRESSIONTARGET 0x0002 -#define COMMAND_SETCOLOURPARAMS 0x0004 -#define COMMAND_SETFORMAT 0x0008 -#define COMMAND_PAUSE 0x0010 -#define COMMAND_RESUME 0x0020 -#define COMMAND_SETYUVTHRESH 0x0040 -#define COMMAND_SETECPTIMING 0x0080 -#define COMMAND_SETCOMPRESSIONPARAMS 0x0100 -#define COMMAND_SETEXPOSURE 0x0200 -#define COMMAND_SETCOLOURBALANCE 0x0400 -#define COMMAND_SETSENSORFPS 0x0800 -#define COMMAND_SETAPCOR 0x1000 -#define COMMAND_SETFLICKERCTRL 0x2000 -#define COMMAND_SETVLOFFSET 0x4000 -#define COMMAND_SETLIGHTS 0x8000 - -#define ROUND_UP_EXP_FOR_FLICKER 15 - -/* Constants for automatic frame rate adjustment */ -#define MAX_EXP 302 -#define MAX_EXP_102 255 -#define LOW_EXP 140 -#define VERY_LOW_EXP 70 -#define TC 94 -#define EXP_ACC_DARK 50 -#define EXP_ACC_LIGHT 90 -#define HIGH_COMP_102 160 -#define MAX_COMP 239 -#define DARK_TIME 3 -#define LIGHT_TIME 3 - -/* Maximum number of 10ms loops to wait for the stream to become ready */ -#define READY_TIMEOUT 100 - -/* Developer's Guide Table 5 p 3-34 - * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/ -static u8 flicker_jumps[2][2][4] = -{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } }, - { { 64, 32, 16, 8 }, { 76, 38, 19, 9} } -}; - -/* forward declaration of local function */ -static void reset_camera_struct(struct cam_data *cam); -static int find_over_exposure(int brightness); -static void set_flicker(struct cam_params *params, volatile u32 *command_flags, - int on); - - -/********************************************************************** - * - * Memory management - * - **********************************************************************/ -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - -/********************************************************************** - * - * /proc interface - * - **********************************************************************/ -#ifdef CONFIG_PROC_FS -static struct proc_dir_entry *cpia_proc_root=NULL; - -static int cpia_proc_show(struct seq_file *m, void *v) -{ - struct cam_data *cam = m->private; - int tmp; - char tmpstr[29]; - - seq_printf(m, "read-only\n-----------------------\n"); - seq_printf(m, "V4L Driver version: %d.%d.%d\n", - CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); - seq_printf(m, "CPIA Version: %d.%02d (%d.%d)\n", - cam->params.version.firmwareVersion, - cam->params.version.firmwareRevision, - cam->params.version.vcVersion, - cam->params.version.vcRevision); - seq_printf(m, "CPIA PnP-ID: %04x:%04x:%04x\n", - cam->params.pnpID.vendor, cam->params.pnpID.product, - cam->params.pnpID.deviceRevision); - seq_printf(m, "VP-Version: %d.%d %04x\n", - cam->params.vpVersion.vpVersion, - cam->params.vpVersion.vpRevision, - cam->params.vpVersion.cameraHeadID); - - seq_printf(m, "system_state: %#04x\n", - cam->params.status.systemState); - seq_printf(m, "grab_state: %#04x\n", - cam->params.status.grabState); - seq_printf(m, "stream_state: %#04x\n", - cam->params.status.streamState); - seq_printf(m, "fatal_error: %#04x\n", - cam->params.status.fatalError); - seq_printf(m, "cmd_error: %#04x\n", - cam->params.status.cmdError); - seq_printf(m, "debug_flags: %#04x\n", - cam->params.status.debugFlags); - seq_printf(m, "vp_status: %#04x\n", - cam->params.status.vpStatus); - seq_printf(m, "error_code: %#04x\n", - cam->params.status.errorCode); - /* QX3 specific entries */ - if (cam->params.qx3.qx3_detected) { - seq_printf(m, "button: %4d\n", - cam->params.qx3.button); - seq_printf(m, "cradled: %4d\n", - cam->params.qx3.cradled); - } - seq_printf(m, "video_size: %s\n", - cam->params.format.videoSize == VIDEOSIZE_CIF ? - "CIF " : "QCIF"); - seq_printf(m, "roi: (%3d, %3d) to (%3d, %3d)\n", - cam->params.roi.colStart*8, - cam->params.roi.rowStart*4, - cam->params.roi.colEnd*8, - cam->params.roi.rowEnd*4); - seq_printf(m, "actual_fps: %3d\n", cam->fps); - seq_printf(m, "transfer_rate: %4dkB/s\n", - cam->transfer_rate); - - seq_printf(m, "\nread-write\n"); - seq_printf(m, "----------------------- current min" - " max default comment\n"); - seq_printf(m, "brightness: %8d %8d %8d %8d\n", - cam->params.colourParams.brightness, 0, 100, 50); - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) - /* 1-02 firmware limits contrast to 80 */ - tmp = 80; - else - tmp = 96; - - seq_printf(m, "contrast: %8d %8d %8d %8d" - " steps of 8\n", - cam->params.colourParams.contrast, 0, tmp, 48); - seq_printf(m, "saturation: %8d %8d %8d %8d\n", - cam->params.colourParams.saturation, 0, 100, 50); - tmp = (25000+5000*cam->params.sensorFps.baserate)/ - (1<params.sensorFps.divisor); - seq_printf(m, "sensor_fps: %4d.%03d %8d %8d %8d\n", - tmp/1000, tmp%1000, 3, 30, 15); - seq_printf(m, "stream_start_line: %8d %8d %8d %8d\n", - 2*cam->params.streamStartLine, 0, - cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144, - cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120); - seq_printf(m, "sub_sample: %8s %8s %8s %8s\n", - cam->params.format.subSample == SUBSAMPLE_420 ? - "420" : "422", "420", "422", "422"); - seq_printf(m, "yuv_order: %8s %8s %8s %8s\n", - cam->params.format.yuvOrder == YUVORDER_YUYV ? - "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV"); - seq_printf(m, "ecp_timing: %8s %8s %8s %8s\n", - cam->params.ecpTiming ? "slow" : "normal", "slow", - "normal", "normal"); - - if (cam->params.colourBalance.balanceMode == 2) { - sprintf(tmpstr, "auto"); - } else { - sprintf(tmpstr, "manual"); - } - seq_printf(m, "color_balance_mode: %8s %8s %8s" - " %8s\n", tmpstr, "manual", "auto", "auto"); - seq_printf(m, "red_gain: %8d %8d %8d %8d\n", - cam->params.colourBalance.redGain, 0, 212, 32); - seq_printf(m, "green_gain: %8d %8d %8d %8d\n", - cam->params.colourBalance.greenGain, 0, 212, 6); - seq_printf(m, "blue_gain: %8d %8d %8d %8d\n", - cam->params.colourBalance.blueGain, 0, 212, 92); - - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) - /* 1-02 firmware limits gain to 2 */ - sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2); - else - sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2); - - if (cam->params.exposure.gainMode == 0) - seq_printf(m, "max_gain: unknown %28s" - " powers of 2\n", tmpstr); - else - seq_printf(m, "max_gain: %8d %28s" - " 1,2,4 or 8 \n", - 1<<(cam->params.exposure.gainMode-1), tmpstr); - - switch(cam->params.exposure.expMode) { - case 1: - case 3: - sprintf(tmpstr, "manual"); - break; - case 2: - sprintf(tmpstr, "auto"); - break; - default: - sprintf(tmpstr, "unknown"); - break; - } - seq_printf(m, "exposure_mode: %8s %8s %8s" - " %8s\n", tmpstr, "manual", "auto", "auto"); - seq_printf(m, "centre_weight: %8s %8s %8s %8s\n", - (2-cam->params.exposure.centreWeight) ? "on" : "off", - "off", "on", "on"); - seq_printf(m, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n", - 1<params.exposure.gain, 1, 1); - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) - /* 1-02 firmware limits fineExp/2 to 127 */ - tmp = 254; - else - tmp = 510; - - seq_printf(m, "fine_exp: %8d %8d %8d %8d\n", - cam->params.exposure.fineExp*2, 0, tmp, 0); - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) - /* 1-02 firmware limits coarseExpHi to 0 */ - tmp = MAX_EXP_102; - else - tmp = MAX_EXP; - - seq_printf(m, "coarse_exp: %8d %8d %8d" - " %8d\n", cam->params.exposure.coarseExpLo+ - 256*cam->params.exposure.coarseExpHi, 0, tmp, 185); - seq_printf(m, "red_comp: %8d %8d %8d %8d\n", - cam->params.exposure.redComp, COMP_RED, 255, COMP_RED); - seq_printf(m, "green1_comp: %8d %8d %8d %8d\n", - cam->params.exposure.green1Comp, COMP_GREEN1, 255, - COMP_GREEN1); - seq_printf(m, "green2_comp: %8d %8d %8d %8d\n", - cam->params.exposure.green2Comp, COMP_GREEN2, 255, - COMP_GREEN2); - seq_printf(m, "blue_comp: %8d %8d %8d %8d\n", - cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE); - - seq_printf(m, "apcor_gain1: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain1, 0, 0xff, 0x1c); - seq_printf(m, "apcor_gain2: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain2, 0, 0xff, 0x1a); - seq_printf(m, "apcor_gain4: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain4, 0, 0xff, 0x2d); - seq_printf(m, "apcor_gain8: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain8, 0, 0xff, 0x2a); - seq_printf(m, "vl_offset_gain1: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain1, 0, 255, 24); - seq_printf(m, "vl_offset_gain2: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain2, 0, 255, 28); - seq_printf(m, "vl_offset_gain4: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain4, 0, 255, 30); - seq_printf(m, "vl_offset_gain8: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain8, 0, 255, 30); - seq_printf(m, "flicker_control: %8s %8s %8s %8s\n", - cam->params.flickerControl.flickerMode ? "on" : "off", - "off", "on", "off"); - seq_printf(m, "mains_frequency: %8d %8d %8d %8d" - " only 50/60\n", - cam->mainsFreq ? 60 : 50, 50, 60, 50); - if(cam->params.flickerControl.allowableOverExposure < 0) - seq_printf(m, "allowable_overexposure: %4dauto auto %8d auto\n", - -cam->params.flickerControl.allowableOverExposure, - 255); - else - seq_printf(m, "allowable_overexposure: %8d auto %8d auto\n", - cam->params.flickerControl.allowableOverExposure, - 255); - seq_printf(m, "compression_mode: "); - switch(cam->params.compression.mode) { - case CPIA_COMPRESSION_NONE: - seq_printf(m, "%8s", "none"); - break; - case CPIA_COMPRESSION_AUTO: - seq_printf(m, "%8s", "auto"); - break; - case CPIA_COMPRESSION_MANUAL: - seq_printf(m, "%8s", "manual"); - break; - default: - seq_printf(m, "%8s", "unknown"); - break; - } - seq_printf(m, " none,auto,manual auto\n"); - seq_printf(m, "decimation_enable: %8s %8s %8s %8s\n", - cam->params.compression.decimation == - DECIMATION_ENAB ? "on":"off", "off", "on", - "off"); - seq_printf(m, "compression_target: %9s %9s %9s %9s\n", - cam->params.compressionTarget.frTargeting == - CPIA_COMPRESSION_TARGET_FRAMERATE ? - "framerate":"quality", - "framerate", "quality", "quality"); - seq_printf(m, "target_framerate: %8d %8d %8d %8d\n", - cam->params.compressionTarget.targetFR, 1, 30, 15); - seq_printf(m, "target_quality: %8d %8d %8d %8d\n", - cam->params.compressionTarget.targetQ, 1, 64, 5); - seq_printf(m, "y_threshold: %8d %8d %8d %8d\n", - cam->params.yuvThreshold.yThreshold, 0, 31, 6); - seq_printf(m, "uv_threshold: %8d %8d %8d %8d\n", - cam->params.yuvThreshold.uvThreshold, 0, 31, 6); - seq_printf(m, "hysteresis: %8d %8d %8d %8d\n", - cam->params.compressionParams.hysteresis, 0, 255, 3); - seq_printf(m, "threshold_max: %8d %8d %8d %8d\n", - cam->params.compressionParams.threshMax, 0, 255, 11); - seq_printf(m, "small_step: %8d %8d %8d %8d\n", - cam->params.compressionParams.smallStep, 0, 255, 1); - seq_printf(m, "large_step: %8d %8d %8d %8d\n", - cam->params.compressionParams.largeStep, 0, 255, 3); - seq_printf(m, "decimation_hysteresis: %8d %8d %8d %8d\n", - cam->params.compressionParams.decimationHysteresis, - 0, 255, 2); - seq_printf(m, "fr_diff_step_thresh: %8d %8d %8d %8d\n", - cam->params.compressionParams.frDiffStepThresh, - 0, 255, 5); - seq_printf(m, "q_diff_step_thresh: %8d %8d %8d %8d\n", - cam->params.compressionParams.qDiffStepThresh, - 0, 255, 3); - seq_printf(m, "decimation_thresh_mod: %8d %8d %8d %8d\n", - cam->params.compressionParams.decimationThreshMod, - 0, 255, 2); - /* QX3 specific entries */ - if (cam->params.qx3.qx3_detected) { - seq_printf(m, "toplight: %8s %8s %8s %8s\n", - cam->params.qx3.toplight ? "on" : "off", - "off", "on", "off"); - seq_printf(m, "bottomlight: %8s %8s %8s %8s\n", - cam->params.qx3.bottomlight ? "on" : "off", - "off", "on", "off"); - } - - return 0; -} - -static int cpia_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cpia_proc_show, PDE(inode)->data); -} - -static int match(char *checkstr, char **buffer, size_t *count, - int *find_colon, int *err) -{ - int ret, colon_found = 1; - int len = strlen(checkstr); - ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0); - if (ret) { - *buffer += len; - *count -= len; - if (*find_colon) { - colon_found = 0; - while (*count && (**buffer == ' ' || **buffer == '\t' || - (!colon_found && **buffer == ':'))) { - if (**buffer == ':') - colon_found = 1; - --*count; - ++*buffer; - } - if (!*count || !colon_found) - *err = -EINVAL; - *find_colon = 0; - } - } - return ret; -} - -static unsigned long int value(char **buffer, size_t *count, int *err) -{ - char *p; - unsigned long int ret; - ret = simple_strtoul(*buffer, &p, 0); - if (p == *buffer) - *err = -EINVAL; - else { - *count -= p - *buffer; - *buffer = p; - } - return ret; -} - -static ssize_t cpia_proc_write(struct file *file, const char __user *buf, - size_t count, loff_t *pos) -{ - struct cam_data *cam = PDE(file->f_path.dentry->d_inode)->data; - struct cam_params new_params; - char *page, *buffer; - int retval, find_colon; - int size = count; - unsigned long val = 0; - u32 command_flags = 0; - u8 new_mains; - - /* - * This code to copy from buf to page is shamelessly copied - * from the comx driver - */ - if (count > PAGE_SIZE) { - printk(KERN_ERR "count is %zu > %d!!!\n", count, (int)PAGE_SIZE); - return -ENOSPC; - } - - if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; - - if(copy_from_user(page, buf, count)) - { - retval = -EFAULT; - goto out; - } - - if (page[count-1] == '\n') - page[count-1] = '\0'; - else if (count < PAGE_SIZE) - page[count] = '\0'; - else if (page[count]) { - retval = -EINVAL; - goto out; - } - - buffer = page; - - if (mutex_lock_interruptible(&cam->param_lock)) - return -ERESTARTSYS; - - /* - * Skip over leading whitespace - */ - while (count && isspace(*buffer)) { - --count; - ++buffer; - } - - memcpy(&new_params, &cam->params, sizeof(struct cam_params)); - new_mains = cam->mainsFreq; - -#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval)) -#define VALUE (value(&buffer,&count, &retval)) -#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \ - new_params.version.firmwareRevision == (y)) - - retval = 0; - while (count && !retval) { - find_colon = 1; - if (MATCH("brightness")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 100) - new_params.colourParams.brightness = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURPARAMS; - if(new_params.flickerControl.allowableOverExposure < 0) - new_params.flickerControl.allowableOverExposure = - -find_over_exposure(new_params.colourParams.brightness); - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - - } else if (MATCH("contrast")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 100) { - /* contrast is in steps of 8, so round*/ - val = ((val + 3) / 8) * 8; - /* 1-02 firmware limits contrast to 80*/ - if (FIRMWARE_VERSION(1,2) && val > 80) - val = 80; - - new_params.colourParams.contrast = val; - } else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURPARAMS; - } else if (MATCH("saturation")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 100) - new_params.colourParams.saturation = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURPARAMS; - } else if (MATCH("sensor_fps")) { - if (!retval) - val = VALUE; - - if (!retval) { - /* find values so that sensorFPS is minimized, - * but >= val */ - if (val > 30) - retval = -EINVAL; - else if (val > 25) { - new_params.sensorFps.divisor = 0; - new_params.sensorFps.baserate = 1; - } else if (val > 15) { - new_params.sensorFps.divisor = 0; - new_params.sensorFps.baserate = 0; - } else if (val > 12) { - new_params.sensorFps.divisor = 1; - new_params.sensorFps.baserate = 1; - } else if (val > 7) { - new_params.sensorFps.divisor = 1; - new_params.sensorFps.baserate = 0; - } else if (val > 6) { - new_params.sensorFps.divisor = 2; - new_params.sensorFps.baserate = 1; - } else if (val > 3) { - new_params.sensorFps.divisor = 2; - new_params.sensorFps.baserate = 0; - } else { - new_params.sensorFps.divisor = 3; - /* Either base rate would work here */ - new_params.sensorFps.baserate = 1; - } - new_params.flickerControl.coarseJump = - flicker_jumps[new_mains] - [new_params.sensorFps.baserate] - [new_params.sensorFps.divisor]; - if (new_params.flickerControl.flickerMode) - command_flags |= COMMAND_SETFLICKERCTRL; - } - command_flags |= COMMAND_SETSENSORFPS; - cam->exposure_status = EXPOSURE_NORMAL; - } else if (MATCH("stream_start_line")) { - if (!retval) - val = VALUE; - - if (!retval) { - int max_line = 288; - - if (new_params.format.videoSize == VIDEOSIZE_QCIF) - max_line = 144; - if (val <= max_line) - new_params.streamStartLine = val/2; - else - retval = -EINVAL; - } - } else if (MATCH("sub_sample")) { - if (!retval && MATCH("420")) - new_params.format.subSample = SUBSAMPLE_420; - else if (!retval && MATCH("422")) - new_params.format.subSample = SUBSAMPLE_422; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETFORMAT; - } else if (MATCH("yuv_order")) { - if (!retval && MATCH("YUYV")) - new_params.format.yuvOrder = YUVORDER_YUYV; - else if (!retval && MATCH("UYVY")) - new_params.format.yuvOrder = YUVORDER_UYVY; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETFORMAT; - } else if (MATCH("ecp_timing")) { - if (!retval && MATCH("normal")) - new_params.ecpTiming = 0; - else if (!retval && MATCH("slow")) - new_params.ecpTiming = 1; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETECPTIMING; - } else if (MATCH("color_balance_mode")) { - if (!retval && MATCH("manual")) - new_params.colourBalance.balanceMode = 3; - else if (!retval && MATCH("auto")) - new_params.colourBalance.balanceMode = 2; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETCOLOURBALANCE; - } else if (MATCH("red_gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 212) { - new_params.colourBalance.redGain = val; - new_params.colourBalance.balanceMode = 1; - } else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURBALANCE; - } else if (MATCH("green_gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 212) { - new_params.colourBalance.greenGain = val; - new_params.colourBalance.balanceMode = 1; - } else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURBALANCE; - } else if (MATCH("blue_gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 212) { - new_params.colourBalance.blueGain = val; - new_params.colourBalance.balanceMode = 1; - } else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURBALANCE; - } else if (MATCH("max_gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - /* 1-02 firmware limits gain to 2 */ - if (FIRMWARE_VERSION(1,2) && val > 2) - val = 2; - switch(val) { - case 1: - new_params.exposure.gainMode = 1; - break; - case 2: - new_params.exposure.gainMode = 2; - break; - case 4: - new_params.exposure.gainMode = 3; - break; - case 8: - new_params.exposure.gainMode = 4; - break; - default: - retval = -EINVAL; - break; - } - } - command_flags |= COMMAND_SETEXPOSURE; - } else if (MATCH("exposure_mode")) { - if (!retval && MATCH("auto")) - new_params.exposure.expMode = 2; - else if (!retval && MATCH("manual")) { - if (new_params.exposure.expMode == 2) - new_params.exposure.expMode = 3; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - new_params.flickerControl.flickerMode = 0; - } else - retval = -EINVAL; - - command_flags |= COMMAND_SETEXPOSURE; - } else if (MATCH("centre_weight")) { - if (!retval && MATCH("on")) - new_params.exposure.centreWeight = 1; - else if (!retval && MATCH("off")) - new_params.exposure.centreWeight = 2; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETEXPOSURE; - } else if (MATCH("gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - switch(val) { - case 1: - new_params.exposure.gain = 0; - break; - case 2: - new_params.exposure.gain = 1; - break; - case 4: - new_params.exposure.gain = 2; - break; - case 8: - new_params.exposure.gain = 3; - break; - default: - retval = -EINVAL; - break; - } - new_params.exposure.expMode = 1; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - new_params.flickerControl.flickerMode = 0; - command_flags |= COMMAND_SETEXPOSURE; - if (new_params.exposure.gain > - new_params.exposure.gainMode-1) - retval = -EINVAL; - } - } else if (MATCH("fine_exp")) { - if (!retval) - val = VALUE/2; - - if (!retval) { - if (val < 256) { - /* 1-02 firmware limits fineExp/2 to 127*/ - if (FIRMWARE_VERSION(1,2) && val > 127) - val = 127; - new_params.exposure.fineExp = val; - new_params.exposure.expMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - new_params.flickerControl.flickerMode = 0; - command_flags |= COMMAND_SETFLICKERCTRL; - } else - retval = -EINVAL; - } - } else if (MATCH("coarse_exp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= MAX_EXP) { - if (FIRMWARE_VERSION(1,2) && - val > MAX_EXP_102) - val = MAX_EXP_102; - new_params.exposure.coarseExpLo = - val & 0xff; - new_params.exposure.coarseExpHi = - val >> 8; - new_params.exposure.expMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - new_params.flickerControl.flickerMode = 0; - command_flags |= COMMAND_SETFLICKERCTRL; - } else - retval = -EINVAL; - } - } else if (MATCH("red_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_RED && val <= 255) { - new_params.exposure.redComp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("green1_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_GREEN1 && val <= 255) { - new_params.exposure.green1Comp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("green2_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_GREEN2 && val <= 255) { - new_params.exposure.green2Comp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("blue_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_BLUE && val <= 255) { - new_params.exposure.blueComp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("apcor_gain1")) { - if (!retval) - val = VALUE; - - if (!retval) { - command_flags |= COMMAND_SETAPCOR; - if (val <= 0xff) - new_params.apcor.gain1 = val; - else - retval = -EINVAL; - } - } else if (MATCH("apcor_gain2")) { - if (!retval) - val = VALUE; - - if (!retval) { - command_flags |= COMMAND_SETAPCOR; - if (val <= 0xff) - new_params.apcor.gain2 = val; - else - retval = -EINVAL; - } - } else if (MATCH("apcor_gain4")) { - if (!retval) - val = VALUE; - - if (!retval) { - command_flags |= COMMAND_SETAPCOR; - if (val <= 0xff) - new_params.apcor.gain4 = val; - else - retval = -EINVAL; - } - } else if (MATCH("apcor_gain8")) { - if (!retval) - val = VALUE; - - if (!retval) { - command_flags |= COMMAND_SETAPCOR; - if (val <= 0xff) - new_params.apcor.gain8 = val; - else - retval = -EINVAL; - } - } else if (MATCH("vl_offset_gain1")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.vlOffset.gain1 = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETVLOFFSET; - } else if (MATCH("vl_offset_gain2")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.vlOffset.gain2 = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETVLOFFSET; - } else if (MATCH("vl_offset_gain4")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.vlOffset.gain4 = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETVLOFFSET; - } else if (MATCH("vl_offset_gain8")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.vlOffset.gain8 = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETVLOFFSET; - } else if (MATCH("flicker_control")) { - if (!retval && MATCH("on")) { - set_flicker(&new_params, &command_flags, 1); - } else if (!retval && MATCH("off")) { - set_flicker(&new_params, &command_flags, 0); - } else - retval = -EINVAL; - - command_flags |= COMMAND_SETFLICKERCTRL; - } else if (MATCH("mains_frequency")) { - if (!retval && MATCH("50")) { - new_mains = 0; - new_params.flickerControl.coarseJump = - flicker_jumps[new_mains] - [new_params.sensorFps.baserate] - [new_params.sensorFps.divisor]; - if (new_params.flickerControl.flickerMode) - command_flags |= COMMAND_SETFLICKERCTRL; - } else if (!retval && MATCH("60")) { - new_mains = 1; - new_params.flickerControl.coarseJump = - flicker_jumps[new_mains] - [new_params.sensorFps.baserate] - [new_params.sensorFps.divisor]; - if (new_params.flickerControl.flickerMode) - command_flags |= COMMAND_SETFLICKERCTRL; - } else - retval = -EINVAL; - } else if (MATCH("allowable_overexposure")) { - if (!retval && MATCH("auto")) { - new_params.flickerControl.allowableOverExposure = - -find_over_exposure(new_params.colourParams.brightness); - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - } else { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) { - new_params.flickerControl. - allowableOverExposure = val; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - } else - retval = -EINVAL; - } - } - } else if (MATCH("compression_mode")) { - if (!retval && MATCH("none")) - new_params.compression.mode = - CPIA_COMPRESSION_NONE; - else if (!retval && MATCH("auto")) - new_params.compression.mode = - CPIA_COMPRESSION_AUTO; - else if (!retval && MATCH("manual")) - new_params.compression.mode = - CPIA_COMPRESSION_MANUAL; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETCOMPRESSION; - } else if (MATCH("decimation_enable")) { - if (!retval && MATCH("off")) - new_params.compression.decimation = 0; - else if (!retval && MATCH("on")) - new_params.compression.decimation = 1; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETCOMPRESSION; - } else if (MATCH("compression_target")) { - if (!retval && MATCH("quality")) - new_params.compressionTarget.frTargeting = - CPIA_COMPRESSION_TARGET_QUALITY; - else if (!retval && MATCH("framerate")) - new_params.compressionTarget.frTargeting = - CPIA_COMPRESSION_TARGET_FRAMERATE; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETCOMPRESSIONTARGET; - } else if (MATCH("target_framerate")) { - if (!retval) - val = VALUE; - - if (!retval) { - if(val > 0 && val <= 30) - new_params.compressionTarget.targetFR = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONTARGET; - } else if (MATCH("target_quality")) { - if (!retval) - val = VALUE; - - if (!retval) { - if(val > 0 && val <= 64) - new_params.compressionTarget.targetQ = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONTARGET; - } else if (MATCH("y_threshold")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val < 32) - new_params.yuvThreshold.yThreshold = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETYUVTHRESH; - } else if (MATCH("uv_threshold")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val < 32) - new_params.yuvThreshold.uvThreshold = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETYUVTHRESH; - } else if (MATCH("hysteresis")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.hysteresis = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("threshold_max")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.threshMax = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("small_step")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.smallStep = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("large_step")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.largeStep = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("decimation_hysteresis")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.decimationHysteresis = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("fr_diff_step_thresh")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.frDiffStepThresh = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("q_diff_step_thresh")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.qDiffStepThresh = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("decimation_thresh_mod")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.decimationThreshMod = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("toplight")) { - if (!retval && MATCH("on")) - new_params.qx3.toplight = 1; - else if (!retval && MATCH("off")) - new_params.qx3.toplight = 0; - else - retval = -EINVAL; - command_flags |= COMMAND_SETLIGHTS; - } else if (MATCH("bottomlight")) { - if (!retval && MATCH("on")) - new_params.qx3.bottomlight = 1; - else if (!retval && MATCH("off")) - new_params.qx3.bottomlight = 0; - else - retval = -EINVAL; - command_flags |= COMMAND_SETLIGHTS; - } else { - DBG("No match found\n"); - retval = -EINVAL; - } - - if (!retval) { - while (count && isspace(*buffer) && *buffer != '\n') { - --count; - ++buffer; - } - if (count) { - if (*buffer == '\0' && count != 1) - retval = -EINVAL; - else if (*buffer != '\n' && *buffer != ';' && - *buffer != '\0') - retval = -EINVAL; - else { - --count; - ++buffer; - } - } - } - } -#undef MATCH -#undef VALUE -#undef FIRMWARE_VERSION - if (!retval) { - if (command_flags & COMMAND_SETCOLOURPARAMS) { - /* Adjust cam->vp to reflect these changes */ - cam->vp.brightness = - new_params.colourParams.brightness*65535/100; - cam->vp.contrast = - new_params.colourParams.contrast*65535/100; - cam->vp.colour = - new_params.colourParams.saturation*65535/100; - } - if((command_flags & COMMAND_SETEXPOSURE) && - new_params.exposure.expMode == 2) - cam->exposure_status = EXPOSURE_NORMAL; - - memcpy(&cam->params, &new_params, sizeof(struct cam_params)); - cam->mainsFreq = new_mains; - cam->cmd_queue |= command_flags; - retval = size; - } else - DBG("error: %d\n", retval); - - mutex_unlock(&cam->param_lock); - -out: - free_page((unsigned long)page); - return retval; -} - -static const struct file_operations cpia_proc_fops = { - .owner = THIS_MODULE, - .open = cpia_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = cpia_proc_write, -}; - -static void create_proc_cpia_cam(struct cam_data *cam) -{ - struct proc_dir_entry *ent; - - if (!cpia_proc_root || !cam) - return; - - ent = proc_create_data(video_device_node_name(&cam->vdev), - S_IRUGO|S_IWUSR, cpia_proc_root, - &cpia_proc_fops, cam); - if (!ent) - return; - - /* - size of the proc entry is 3736 bytes for the standard webcam; - the extra features of the QX3 microscope add 189 bytes. - (we have not yet probed the camera to see which type it is). - */ - ent->size = 3736 + 189; - cam->proc_entry = ent; -} - -static void destroy_proc_cpia_cam(struct cam_data *cam) -{ - if (!cam || !cam->proc_entry) - return; - - remove_proc_entry(video_device_node_name(&cam->vdev), cpia_proc_root); - cam->proc_entry = NULL; -} - -static void proc_cpia_create(void) -{ - cpia_proc_root = proc_mkdir("cpia", NULL); - - if (!cpia_proc_root) - LOG("Unable to initialise /proc/cpia\n"); -} - -static void __exit proc_cpia_destroy(void) -{ - remove_proc_entry("cpia", NULL); -} -#endif /* CONFIG_PROC_FS */ - -/* ----------------------- debug functions ---------------------- */ - -#define printstatus(cam) \ - DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\ - cam->params.status.systemState, cam->params.status.grabState, \ - cam->params.status.streamState, cam->params.status.fatalError, \ - cam->params.status.cmdError, cam->params.status.debugFlags, \ - cam->params.status.vpStatus, cam->params.status.errorCode); - -/* ----------------------- v4l helpers -------------------------- */ - -/* supported frame palettes and depths */ -static inline int valid_mode(u16 palette, u16 depth) -{ - if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) || - (palette == VIDEO_PALETTE_YUYV && depth == 16)) - return 1; - - if (colorspace_conv) - return (palette == VIDEO_PALETTE_GREY && depth == 8) || - (palette == VIDEO_PALETTE_RGB555 && depth == 16) || - (palette == VIDEO_PALETTE_RGB565 && depth == 16) || - (palette == VIDEO_PALETTE_RGB24 && depth == 24) || - (palette == VIDEO_PALETTE_RGB32 && depth == 32) || - (palette == VIDEO_PALETTE_UYVY && depth == 16); - - return 0; -} - -static int match_videosize( int width, int height ) -{ - /* return the best match, where 'best' is as always - * the largest that is not bigger than what is requested. */ - if (width>=352 && height>=288) - return VIDEOSIZE_352_288; /* CIF */ - - if (width>=320 && height>=240) - return VIDEOSIZE_320_240; /* SIF */ - - if (width>=288 && height>=216) - return VIDEOSIZE_288_216; - - if (width>=256 && height>=192) - return VIDEOSIZE_256_192; - - if (width>=224 && height>=168) - return VIDEOSIZE_224_168; - - if (width>=192 && height>=144) - return VIDEOSIZE_192_144; - - if (width>=176 && height>=144) - return VIDEOSIZE_176_144; /* QCIF */ - - if (width>=160 && height>=120) - return VIDEOSIZE_160_120; /* QSIF */ - - if (width>=128 && height>=96) - return VIDEOSIZE_128_96; - - if (width>=88 && height>=72) - return VIDEOSIZE_88_72; - - if (width>=64 && height>=48) - return VIDEOSIZE_64_48; - - if (width>=48 && height>=48) - return VIDEOSIZE_48_48; - - return -1; -} - -/* these are the capture sizes we support */ -static void set_vw_size(struct cam_data *cam) -{ - /* the col/row/start/end values are the result of simple math */ - /* study the SetROI-command in cpia developers guide p 2-22 */ - /* streamStartLine is set to the recommended value in the cpia */ - /* developers guide p 3-37 */ - switch(cam->video_size) { - case VIDEOSIZE_CIF: - cam->vw.width = 352; - cam->vw.height = 288; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=0; - cam->params.roi.rowStart=0; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_SIF: - cam->vw.width = 320; - cam->vw.height = 240; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=2; - cam->params.roi.rowStart=6; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_288_216: - cam->vw.width = 288; - cam->vw.height = 216; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=4; - cam->params.roi.rowStart=9; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_256_192: - cam->vw.width = 256; - cam->vw.height = 192; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=6; - cam->params.roi.rowStart=12; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_224_168: - cam->vw.width = 224; - cam->vw.height = 168; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=8; - cam->params.roi.rowStart=15; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_192_144: - cam->vw.width = 192; - cam->vw.height = 144; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=10; - cam->params.roi.rowStart=18; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_QCIF: - cam->vw.width = 176; - cam->vw.height = 144; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=0; - cam->params.roi.rowStart=0; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_QSIF: - cam->vw.width = 160; - cam->vw.height = 120; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=1; - cam->params.roi.rowStart=3; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_128_96: - cam->vw.width = 128; - cam->vw.height = 96; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=3; - cam->params.roi.rowStart=6; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_88_72: - cam->vw.width = 88; - cam->vw.height = 72; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=5; - cam->params.roi.rowStart=9; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_64_48: - cam->vw.width = 64; - cam->vw.height = 48; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=7; - cam->params.roi.rowStart=12; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_48_48: - cam->vw.width = 48; - cam->vw.height = 48; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=8; - cam->params.roi.rowStart=6; - cam->params.streamStartLine = 60; - break; - default: - LOG("bad videosize value: %d\n", cam->video_size); - return; - } - - if(cam->vc.width == 0) - cam->vc.width = cam->vw.width; - if(cam->vc.height == 0) - cam->vc.height = cam->vw.height; - - cam->params.roi.colStart += cam->vc.x >> 3; - cam->params.roi.colEnd = cam->params.roi.colStart + - (cam->vc.width >> 3); - cam->params.roi.rowStart += cam->vc.y >> 2; - cam->params.roi.rowEnd = cam->params.roi.rowStart + - (cam->vc.height >> 2); - - return; -} - -static int allocate_frame_buf(struct cam_data *cam) -{ - int i; - - cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE); - if (!cam->frame_buf) - return -ENOBUFS; - - for (i = 0; i < FRAME_NUM; i++) - cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE; - - return 0; -} - -static int free_frame_buf(struct cam_data *cam) -{ - int i; - - rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE); - cam->frame_buf = NULL; - for (i=0; i < FRAME_NUM; i++) - cam->frame[i].data = NULL; - - return 0; -} - - -static inline void free_frames(struct cpia_frame frame[FRAME_NUM]) -{ - int i; - - for (i=0; i < FRAME_NUM; i++) - frame[i].state = FRAME_UNUSED; - return; -} - -/********************************************************************** - * - * General functions - * - **********************************************************************/ -/* send an arbitrary command to the camera */ -static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d) -{ - int retval, datasize; - u8 cmd[8], data[8]; - - switch(command) { - case CPIA_COMMAND_GetCPIAVersion: - case CPIA_COMMAND_GetPnPID: - case CPIA_COMMAND_GetCameraStatus: - case CPIA_COMMAND_GetVPVersion: - datasize=8; - break; - case CPIA_COMMAND_GetColourParams: - case CPIA_COMMAND_GetColourBalance: - case CPIA_COMMAND_GetExposure: - mutex_lock(&cam->param_lock); - datasize=8; - break; - case CPIA_COMMAND_ReadMCPorts: - case CPIA_COMMAND_ReadVCRegs: - datasize = 4; - break; - default: - datasize=0; - break; - } - - cmd[0] = command>>8; - cmd[1] = command&0xff; - cmd[2] = a; - cmd[3] = b; - cmd[4] = c; - cmd[5] = d; - cmd[6] = datasize; - cmd[7] = 0; - - retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); - if (retval) { - DBG("%x - failed, retval=%d\n", command, retval); - if (command == CPIA_COMMAND_GetColourParams || - command == CPIA_COMMAND_GetColourBalance || - command == CPIA_COMMAND_GetExposure) - mutex_unlock(&cam->param_lock); - } else { - switch(command) { - case CPIA_COMMAND_GetCPIAVersion: - cam->params.version.firmwareVersion = data[0]; - cam->params.version.firmwareRevision = data[1]; - cam->params.version.vcVersion = data[2]; - cam->params.version.vcRevision = data[3]; - break; - case CPIA_COMMAND_GetPnPID: - cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8); - cam->params.pnpID.product = data[2]+(((u16)data[3])<<8); - cam->params.pnpID.deviceRevision = - data[4]+(((u16)data[5])<<8); - break; - case CPIA_COMMAND_GetCameraStatus: - cam->params.status.systemState = data[0]; - cam->params.status.grabState = data[1]; - cam->params.status.streamState = data[2]; - cam->params.status.fatalError = data[3]; - cam->params.status.cmdError = data[4]; - cam->params.status.debugFlags = data[5]; - cam->params.status.vpStatus = data[6]; - cam->params.status.errorCode = data[7]; - break; - case CPIA_COMMAND_GetVPVersion: - cam->params.vpVersion.vpVersion = data[0]; - cam->params.vpVersion.vpRevision = data[1]; - cam->params.vpVersion.cameraHeadID = - data[2]+(((u16)data[3])<<8); - break; - case CPIA_COMMAND_GetColourParams: - cam->params.colourParams.brightness = data[0]; - cam->params.colourParams.contrast = data[1]; - cam->params.colourParams.saturation = data[2]; - mutex_unlock(&cam->param_lock); - break; - case CPIA_COMMAND_GetColourBalance: - cam->params.colourBalance.redGain = data[0]; - cam->params.colourBalance.greenGain = data[1]; - cam->params.colourBalance.blueGain = data[2]; - mutex_unlock(&cam->param_lock); - break; - case CPIA_COMMAND_GetExposure: - cam->params.exposure.gain = data[0]; - cam->params.exposure.fineExp = data[1]; - cam->params.exposure.coarseExpLo = data[2]; - cam->params.exposure.coarseExpHi = data[3]; - cam->params.exposure.redComp = data[4]; - cam->params.exposure.green1Comp = data[5]; - cam->params.exposure.green2Comp = data[6]; - cam->params.exposure.blueComp = data[7]; - mutex_unlock(&cam->param_lock); - break; - - case CPIA_COMMAND_ReadMCPorts: - if (!cam->params.qx3.qx3_detected) - break; - /* test button press */ - cam->params.qx3.button = ((data[1] & 0x02) == 0); - if (cam->params.qx3.button) { - /* button pressed - unlock the latch */ - do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0); - do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0); - } - - /* test whether microscope is cradled */ - cam->params.qx3.cradled = ((data[2] & 0x40) == 0); - break; - - default: - break; - } - } - return retval; -} - -/* send a command to the camera with an additional data transaction */ -static int do_command_extended(struct cam_data *cam, u16 command, - u8 a, u8 b, u8 c, u8 d, - u8 e, u8 f, u8 g, u8 h, - u8 i, u8 j, u8 k, u8 l) -{ - int retval; - u8 cmd[8], data[8]; - - cmd[0] = command>>8; - cmd[1] = command&0xff; - cmd[2] = a; - cmd[3] = b; - cmd[4] = c; - cmd[5] = d; - cmd[6] = 8; - cmd[7] = 0; - data[0] = e; - data[1] = f; - data[2] = g; - data[3] = h; - data[4] = i; - data[5] = j; - data[6] = k; - data[7] = l; - - retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); - if (retval) - DBG("%x - failed\n", command); - - return retval; -} - -/********************************************************************** - * - * Colorspace conversion - * - **********************************************************************/ -#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) - -static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt, - int linesize, int mmap_kludge) -{ - int y, u, v, r, g, b, y1; - - /* Odd lines use the same u and v as the previous line. - * Because of compression, it is necessary to get this - * information from the decoded image. */ - switch(out_fmt) { - case VIDEO_PALETTE_RGB555: - y = (*yuv++ - 16) * 76310; - y1 = (*yuv - 16) * 76310; - r = ((*(rgb+1-linesize)) & 0x7c) << 1; - g = ((*(rgb-linesize)) & 0xe0) >> 4 | - ((*(rgb+1-linesize)) & 0x03) << 6; - b = ((*(rgb-linesize)) & 0x1f) << 3; - u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; - v = (157968 * r - 132278 * g - 25690 * b) / 5366159; - r = 104635 * v; - g = -25690 * u - 53294 * v; - b = 132278 * u; - *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); - *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); - *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); - *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); - return 4; - case VIDEO_PALETTE_RGB565: - y = (*yuv++ - 16) * 76310; - y1 = (*yuv - 16) * 76310; - r = (*(rgb+1-linesize)) & 0xf8; - g = ((*(rgb-linesize)) & 0xe0) >> 3 | - ((*(rgb+1-linesize)) & 0x07) << 5; - b = ((*(rgb-linesize)) & 0x1f) << 3; - u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; - v = (157968 * r - 132278 * g - 25690 * b) / 5366159; - r = 104635 * v; - g = -25690 * u - 53294 * v; - b = 132278 * u; - *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); - *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); - *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); - *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); - return 4; - break; - case VIDEO_PALETTE_RGB24: - case VIDEO_PALETTE_RGB32: - y = (*yuv++ - 16) * 76310; - y1 = (*yuv - 16) * 76310; - if (mmap_kludge) { - r = *(rgb+2-linesize); - g = *(rgb+1-linesize); - b = *(rgb-linesize); - } else { - r = *(rgb-linesize); - g = *(rgb+1-linesize); - b = *(rgb+2-linesize); - } - u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; - v = (157968 * r - 132278 * g - 25690 * b) / 5366159; - r = 104635 * v; - g = -25690 * u + -53294 * v; - b = 132278 * u; - if (mmap_kludge) { - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(r+y); - if(out_fmt == VIDEO_PALETTE_RGB32) - rgb++; - *rgb++ = LIMIT(b+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(r+y1); - } else { - *rgb++ = LIMIT(r+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(b+y); - if(out_fmt == VIDEO_PALETTE_RGB32) - rgb++; - *rgb++ = LIMIT(r+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(b+y1); - } - if(out_fmt == VIDEO_PALETTE_RGB32) - return 8; - return 6; - case VIDEO_PALETTE_YUV422: - case VIDEO_PALETTE_YUYV: - y = *yuv++; - u = *(rgb+1-linesize); - y1 = *yuv; - v = *(rgb+3-linesize); - *rgb++ = y; - *rgb++ = u; - *rgb++ = y1; - *rgb = v; - return 4; - case VIDEO_PALETTE_UYVY: - u = *(rgb-linesize); - y = *yuv++; - v = *(rgb+2-linesize); - y1 = *yuv; - *rgb++ = u; - *rgb++ = y; - *rgb++ = v; - *rgb = y1; - return 4; - case VIDEO_PALETTE_GREY: - *rgb++ = *yuv++; - *rgb = *yuv; - return 2; - default: - DBG("Empty: %d\n", out_fmt); - return 0; - } -} - - -static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt, - int in_uyvy, int mmap_kludge) -{ - int y, u, v, r, g, b, y1; - - switch(out_fmt) { - case VIDEO_PALETTE_RGB555: - case VIDEO_PALETTE_RGB565: - case VIDEO_PALETTE_RGB24: - case VIDEO_PALETTE_RGB32: - if (in_uyvy) { - u = *yuv++ - 128; - y = (*yuv++ - 16) * 76310; - v = *yuv++ - 128; - y1 = (*yuv - 16) * 76310; - } else { - y = (*yuv++ - 16) * 76310; - u = *yuv++ - 128; - y1 = (*yuv++ - 16) * 76310; - v = *yuv - 128; - } - r = 104635 * v; - g = -25690 * u + -53294 * v; - b = 132278 * u; - break; - default: - y = *yuv++; - u = *yuv++; - y1 = *yuv++; - v = *yuv; - /* Just to avoid compiler warnings */ - r = 0; - g = 0; - b = 0; - break; - } - switch(out_fmt) { - case VIDEO_PALETTE_RGB555: - *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); - *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); - *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); - *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); - return 4; - case VIDEO_PALETTE_RGB565: - *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); - *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); - *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); - *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); - return 4; - case VIDEO_PALETTE_RGB24: - if (mmap_kludge) { - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(r+y); - *rgb++ = LIMIT(b+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(r+y1); - } else { - *rgb++ = LIMIT(r+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(r+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(b+y1); - } - return 6; - case VIDEO_PALETTE_RGB32: - if (mmap_kludge) { - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(r+y); - rgb++; - *rgb++ = LIMIT(b+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(r+y1); - } else { - *rgb++ = LIMIT(r+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(b+y); - rgb++; - *rgb++ = LIMIT(r+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(b+y1); - } - return 8; - case VIDEO_PALETTE_GREY: - *rgb++ = y; - *rgb = y1; - return 2; - case VIDEO_PALETTE_YUV422: - case VIDEO_PALETTE_YUYV: - *rgb++ = y; - *rgb++ = u; - *rgb++ = y1; - *rgb = v; - return 4; - case VIDEO_PALETTE_UYVY: - *rgb++ = u; - *rgb++ = y; - *rgb++ = v; - *rgb = y1; - return 4; - default: - DBG("Empty: %d\n", out_fmt); - return 0; - } -} - -static int skipcount(int count, int fmt) -{ - switch(fmt) { - case VIDEO_PALETTE_GREY: - return count; - case VIDEO_PALETTE_RGB555: - case VIDEO_PALETTE_RGB565: - case VIDEO_PALETTE_YUV422: - case VIDEO_PALETTE_YUYV: - case VIDEO_PALETTE_UYVY: - return 2*count; - case VIDEO_PALETTE_RGB24: - return 3*count; - case VIDEO_PALETTE_RGB32: - return 4*count; - default: - return 0; - } -} - -static int parse_picture(struct cam_data *cam, int size) -{ - u8 *obuf, *ibuf, *end_obuf; - int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt; - int rows, cols, linesize, subsample_422; - - /* make sure params don't change while we are decoding */ - mutex_lock(&cam->param_lock); - - obuf = cam->decompressed_frame.data; - end_obuf = obuf+CPIA_MAX_FRAME_SIZE; - ibuf = cam->raw_image; - origsize = size; - out_fmt = cam->vp.palette; - - if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) { - LOG("header not found\n"); - mutex_unlock(&cam->param_lock); - return -1; - } - - if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) { - LOG("wrong video size\n"); - mutex_unlock(&cam->param_lock); - return -1; - } - - if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) { - LOG("illegal subtype %d\n",ibuf[17]); - mutex_unlock(&cam->param_lock); - return -1; - } - subsample_422 = ibuf[17] == SUBSAMPLE_422; - - if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) { - LOG("illegal yuvorder %d\n",ibuf[18]); - mutex_unlock(&cam->param_lock); - return -1; - } - in_uyvy = ibuf[18] == YUVORDER_UYVY; - - if ((ibuf[24] != cam->params.roi.colStart) || - (ibuf[25] != cam->params.roi.colEnd) || - (ibuf[26] != cam->params.roi.rowStart) || - (ibuf[27] != cam->params.roi.rowEnd)) { - LOG("ROI mismatch\n"); - mutex_unlock(&cam->param_lock); - return -1; - } - cols = 8*(ibuf[25] - ibuf[24]); - rows = 4*(ibuf[27] - ibuf[26]); - - - if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) { - LOG("illegal compression %d\n",ibuf[28]); - mutex_unlock(&cam->param_lock); - return -1; - } - compressed = (ibuf[28] == COMPRESSED); - - if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) { - LOG("illegal decimation %d\n",ibuf[29]); - mutex_unlock(&cam->param_lock); - return -1; - } - decimation = (ibuf[29] == DECIMATION_ENAB); - - cam->params.yuvThreshold.yThreshold = ibuf[30]; - cam->params.yuvThreshold.uvThreshold = ibuf[31]; - cam->params.status.systemState = ibuf[32]; - cam->params.status.grabState = ibuf[33]; - cam->params.status.streamState = ibuf[34]; - cam->params.status.fatalError = ibuf[35]; - cam->params.status.cmdError = ibuf[36]; - cam->params.status.debugFlags = ibuf[37]; - cam->params.status.vpStatus = ibuf[38]; - cam->params.status.errorCode = ibuf[39]; - cam->fps = ibuf[41]; - mutex_unlock(&cam->param_lock); - - linesize = skipcount(cols, out_fmt); - ibuf += FRAME_HEADER_SIZE; - size -= FRAME_HEADER_SIZE; - ll = ibuf[0] | (ibuf[1] << 8); - ibuf += 2; - even_line = 1; - - while (size > 0) { - size -= (ll+2); - if (size < 0) { - LOG("Insufficient data in buffer\n"); - return -1; - } - - while (ll > 1) { - if (!compressed || (compressed && !(*ibuf & 1))) { - if(subsample_422 || even_line) { - obuf += yuvconvert(ibuf, obuf, out_fmt, - in_uyvy, cam->mmap_kludge); - ibuf += 4; - ll -= 4; - } else { - /* SUBSAMPLE_420 on an odd line */ - obuf += convert420(ibuf, obuf, - out_fmt, linesize, - cam->mmap_kludge); - ibuf += 2; - ll -= 2; - } - } else { - /*skip compressed interval from previous frame*/ - obuf += skipcount(*ibuf >> 1, out_fmt); - if (obuf > end_obuf) { - LOG("Insufficient buffer size\n"); - return -1; - } - ++ibuf; - ll--; - } - } - if (ll == 1) { - if (*ibuf != EOL) { - DBG("EOL not found giving up after %d/%d" - " bytes\n", origsize-size, origsize); - return -1; - } - - ++ibuf; /* skip over EOL */ - - if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) && - (ibuf[2] == EOI) && (ibuf[3] == EOI)) { - size -= 4; - break; - } - - if(decimation) { - /* skip the odd lines for now */ - obuf += linesize; - } - - if (size > 1) { - ll = ibuf[0] | (ibuf[1] << 8); - ibuf += 2; /* skip over line length */ - } - if(!decimation) - even_line = !even_line; - } else { - LOG("line length was not 1 but %d after %d/%d bytes\n", - ll, origsize-size, origsize); - return -1; - } - } - - if(decimation) { - /* interpolate odd rows */ - int i, j; - u8 *prev, *next; - prev = cam->decompressed_frame.data; - obuf = prev+linesize; - next = obuf+linesize; - for(i=1; idecompressed_frame.count = obuf-cam->decompressed_frame.data; - - return cam->decompressed_frame.count; -} - -/* InitStreamCap wrapper to select correct start line */ -static inline int init_stream_cap(struct cam_data *cam) -{ - return do_command(cam, CPIA_COMMAND_InitStreamCap, - 0, cam->params.streamStartLine, 0, 0); -} - - -/* find_over_exposure - * Finds a suitable value of OverExposure for use with SetFlickerCtrl - * Some calculation is required because this value changes with the brightness - * set with SetColourParameters - * - * Parameters: Brightness - last brightness value set with SetColourParameters - * - * Returns: OverExposure value to use with SetFlickerCtrl - */ -#define FLICKER_MAX_EXPOSURE 250 -#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146 -#define FLICKER_BRIGHTNESS_CONSTANT 59 -static int find_over_exposure(int brightness) -{ - int MaxAllowableOverExposure, OverExposure; - - MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness - - FLICKER_BRIGHTNESS_CONSTANT; - - if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) { - OverExposure = MaxAllowableOverExposure; - } else { - OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE; - } - - return OverExposure; -} -#undef FLICKER_MAX_EXPOSURE -#undef FLICKER_ALLOWABLE_OVER_EXPOSURE -#undef FLICKER_BRIGHTNESS_CONSTANT - -/* update various camera modes and settings */ -static void dispatch_commands(struct cam_data *cam) -{ - mutex_lock(&cam->param_lock); - if (cam->cmd_queue==COMMAND_NONE) { - mutex_unlock(&cam->param_lock); - return; - } - DEB_BYTE(cam->cmd_queue); - DEB_BYTE(cam->cmd_queue>>8); - if (cam->cmd_queue & COMMAND_SETFORMAT) { - do_command(cam, CPIA_COMMAND_SetFormat, - cam->params.format.videoSize, - cam->params.format.subSample, - cam->params.format.yuvOrder, 0); - do_command(cam, CPIA_COMMAND_SetROI, - cam->params.roi.colStart, cam->params.roi.colEnd, - cam->params.roi.rowStart, cam->params.roi.rowEnd); - cam->first_frame = 1; - } - - if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS) - do_command(cam, CPIA_COMMAND_SetColourParams, - cam->params.colourParams.brightness, - cam->params.colourParams.contrast, - cam->params.colourParams.saturation, 0); - - if (cam->cmd_queue & COMMAND_SETAPCOR) - do_command(cam, CPIA_COMMAND_SetApcor, - cam->params.apcor.gain1, - cam->params.apcor.gain2, - cam->params.apcor.gain4, - cam->params.apcor.gain8); - - if (cam->cmd_queue & COMMAND_SETVLOFFSET) - do_command(cam, CPIA_COMMAND_SetVLOffset, - cam->params.vlOffset.gain1, - cam->params.vlOffset.gain2, - cam->params.vlOffset.gain4, - cam->params.vlOffset.gain8); - - if (cam->cmd_queue & COMMAND_SETEXPOSURE) { - do_command_extended(cam, CPIA_COMMAND_SetExposure, - cam->params.exposure.gainMode, - 1, - cam->params.exposure.compMode, - cam->params.exposure.centreWeight, - cam->params.exposure.gain, - cam->params.exposure.fineExp, - cam->params.exposure.coarseExpLo, - cam->params.exposure.coarseExpHi, - cam->params.exposure.redComp, - cam->params.exposure.green1Comp, - cam->params.exposure.green2Comp, - cam->params.exposure.blueComp); - if(cam->params.exposure.expMode != 1) { - do_command_extended(cam, CPIA_COMMAND_SetExposure, - 0, - cam->params.exposure.expMode, - 0, 0, - cam->params.exposure.gain, - cam->params.exposure.fineExp, - cam->params.exposure.coarseExpLo, - cam->params.exposure.coarseExpHi, - 0, 0, 0, 0); - } - } - - if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) { - if (cam->params.colourBalance.balanceMode == 1) { - do_command(cam, CPIA_COMMAND_SetColourBalance, - 1, - cam->params.colourBalance.redGain, - cam->params.colourBalance.greenGain, - cam->params.colourBalance.blueGain); - do_command(cam, CPIA_COMMAND_SetColourBalance, - 3, 0, 0, 0); - } - if (cam->params.colourBalance.balanceMode == 2) { - do_command(cam, CPIA_COMMAND_SetColourBalance, - 2, 0, 0, 0); - } - if (cam->params.colourBalance.balanceMode == 3) { - do_command(cam, CPIA_COMMAND_SetColourBalance, - 3, 0, 0, 0); - } - } - - if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET) - do_command(cam, CPIA_COMMAND_SetCompressionTarget, - cam->params.compressionTarget.frTargeting, - cam->params.compressionTarget.targetFR, - cam->params.compressionTarget.targetQ, 0); - - if (cam->cmd_queue & COMMAND_SETYUVTHRESH) - do_command(cam, CPIA_COMMAND_SetYUVThresh, - cam->params.yuvThreshold.yThreshold, - cam->params.yuvThreshold.uvThreshold, 0, 0); - - if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS) - do_command_extended(cam, CPIA_COMMAND_SetCompressionParams, - 0, 0, 0, 0, - cam->params.compressionParams.hysteresis, - cam->params.compressionParams.threshMax, - cam->params.compressionParams.smallStep, - cam->params.compressionParams.largeStep, - cam->params.compressionParams.decimationHysteresis, - cam->params.compressionParams.frDiffStepThresh, - cam->params.compressionParams.qDiffStepThresh, - cam->params.compressionParams.decimationThreshMod); - - if (cam->cmd_queue & COMMAND_SETCOMPRESSION) - do_command(cam, CPIA_COMMAND_SetCompression, - cam->params.compression.mode, - cam->params.compression.decimation, 0, 0); - - if (cam->cmd_queue & COMMAND_SETSENSORFPS) - do_command(cam, CPIA_COMMAND_SetSensorFPS, - cam->params.sensorFps.divisor, - cam->params.sensorFps.baserate, 0, 0); - - if (cam->cmd_queue & COMMAND_SETFLICKERCTRL) - do_command(cam, CPIA_COMMAND_SetFlickerCtrl, - cam->params.flickerControl.flickerMode, - cam->params.flickerControl.coarseJump, - abs(cam->params.flickerControl.allowableOverExposure), - 0); - - if (cam->cmd_queue & COMMAND_SETECPTIMING) - do_command(cam, CPIA_COMMAND_SetECPTiming, - cam->params.ecpTiming, 0, 0, 0); - - if (cam->cmd_queue & COMMAND_PAUSE) - do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); - - if (cam->cmd_queue & COMMAND_RESUME) - init_stream_cap(cam); - - if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected) - { - int p1 = (cam->params.qx3.bottomlight == 0) << 1; - int p2 = (cam->params.qx3.toplight == 0) << 3; - do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0); - do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0); - } - - cam->cmd_queue = COMMAND_NONE; - mutex_unlock(&cam->param_lock); - return; -} - - - -static void set_flicker(struct cam_params *params, volatile u32 *command_flags, - int on) -{ - /* Everything in here is from the Windows driver */ -#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \ - params->version.firmwareRevision == (y)) -/* define for compgain calculation */ -#if 0 -#define COMPGAIN(base, curexp, newexp) \ - (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5) -#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ - (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128)) -#else - /* equivalent functions without floating point math */ -#define COMPGAIN(base, curexp, newexp) \ - (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) ) -#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ - (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128))) -#endif - - - int currentexp = params->exposure.coarseExpLo + - params->exposure.coarseExpHi*256; - int startexp; - if (on) { - int cj = params->flickerControl.coarseJump; - params->flickerControl.flickerMode = 1; - params->flickerControl.disabled = 0; - if(params->exposure.expMode != 2) - *command_flags |= COMMAND_SETEXPOSURE; - params->exposure.expMode = 2; - currentexp = currentexp << params->exposure.gain; - params->exposure.gain = 0; - /* round down current exposure to nearest value */ - startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj; - if(startexp < 1) - startexp = 1; - startexp = (startexp * cj) - 1; - if(FIRMWARE_VERSION(1,2)) - while(startexp > MAX_EXP_102) - startexp -= cj; - else - while(startexp > MAX_EXP) - startexp -= cj; - params->exposure.coarseExpLo = startexp & 0xff; - params->exposure.coarseExpHi = startexp >> 8; - if (currentexp > startexp) { - if (currentexp > (2 * startexp)) - currentexp = 2 * startexp; - params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp); - params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp); - params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp); - params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp); - } else { - params->exposure.redComp = COMP_RED; - params->exposure.green1Comp = COMP_GREEN1; - params->exposure.green2Comp = COMP_GREEN2; - params->exposure.blueComp = COMP_BLUE; - } - if(FIRMWARE_VERSION(1,2)) - params->exposure.compMode = 0; - else - params->exposure.compMode = 1; - - params->apcor.gain1 = 0x18; - params->apcor.gain2 = 0x18; - params->apcor.gain4 = 0x16; - params->apcor.gain8 = 0x14; - *command_flags |= COMMAND_SETAPCOR; - } else { - params->flickerControl.flickerMode = 0; - params->flickerControl.disabled = 1; - /* Coarse = average of equivalent coarse for each comp channel */ - startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp); - startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp); - startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp); - startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp); - startexp = startexp >> 2; - while(startexp > MAX_EXP && - params->exposure.gain < params->exposure.gainMode-1) { - startexp = startexp >> 1; - ++params->exposure.gain; - } - if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102) - startexp = MAX_EXP_102; - if(startexp > MAX_EXP) - startexp = MAX_EXP; - params->exposure.coarseExpLo = startexp&0xff; - params->exposure.coarseExpHi = startexp >> 8; - params->exposure.redComp = COMP_RED; - params->exposure.green1Comp = COMP_GREEN1; - params->exposure.green2Comp = COMP_GREEN2; - params->exposure.blueComp = COMP_BLUE; - params->exposure.compMode = 1; - *command_flags |= COMMAND_SETEXPOSURE; - params->apcor.gain1 = 0x18; - params->apcor.gain2 = 0x16; - params->apcor.gain4 = 0x24; - params->apcor.gain8 = 0x34; - *command_flags |= COMMAND_SETAPCOR; - } - params->vlOffset.gain1 = 20; - params->vlOffset.gain2 = 24; - params->vlOffset.gain4 = 26; - params->vlOffset.gain8 = 26; - *command_flags |= COMMAND_SETVLOFFSET; -#undef FIRMWARE_VERSION -#undef EXP_FROM_COMP -#undef COMPGAIN -} - -#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \ - cam->params.version.firmwareRevision == (y)) -/* monitor the exposure and adjust the sensor frame rate if needed */ -static void monitor_exposure(struct cam_data *cam) -{ - u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8]; - int retval, light_exp, dark_exp, very_dark_exp; - int old_exposure, new_exposure, framerate; - - /* get necessary stats and register settings from camera */ - /* do_command can't handle this, so do it ourselves */ - cmd[0] = CPIA_COMMAND_ReadVPRegs>>8; - cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff; - cmd[2] = 30; - cmd[3] = 4; - cmd[4] = 9; - cmd[5] = 8; - cmd[6] = 8; - cmd[7] = 0; - retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); - if (retval) { - LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n", - retval); - return; - } - exp_acc = data[0]; - bcomp = data[1]; - gain = data[2]; - coarseL = data[3]; - - mutex_lock(&cam->param_lock); - light_exp = cam->params.colourParams.brightness + - TC - 50 + EXP_ACC_LIGHT; - if(light_exp > 255) - light_exp = 255; - dark_exp = cam->params.colourParams.brightness + - TC - 50 - EXP_ACC_DARK; - if(dark_exp < 0) - dark_exp = 0; - very_dark_exp = dark_exp/2; - - old_exposure = cam->params.exposure.coarseExpHi * 256 + - cam->params.exposure.coarseExpLo; - - if(!cam->params.flickerControl.disabled) { - /* Flicker control on */ - int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102; - bcomp += 128; /* decode */ - if(bcomp >= max_comp && exp_acc < dark_exp) { - /* dark */ - if(exp_acc < very_dark_exp) { - /* very dark */ - if(cam->exposure_status == EXPOSURE_VERY_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_DARK; - cam->exposure_count = 1; - } - } else { - /* just dark */ - if(cam->exposure_status == EXPOSURE_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_DARK; - cam->exposure_count = 1; - } - } - } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { - /* light */ - if(old_exposure <= VERY_LOW_EXP) { - /* very light */ - if(cam->exposure_status == EXPOSURE_VERY_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_LIGHT; - cam->exposure_count = 1; - } - } else { - /* just light */ - if(cam->exposure_status == EXPOSURE_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_LIGHT; - cam->exposure_count = 1; - } - } - } else { - /* not dark or light */ - cam->exposure_status = EXPOSURE_NORMAL; - } - } else { - /* Flicker control off */ - if(old_exposure >= MAX_EXP && exp_acc < dark_exp) { - /* dark */ - if(exp_acc < very_dark_exp) { - /* very dark */ - if(cam->exposure_status == EXPOSURE_VERY_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_DARK; - cam->exposure_count = 1; - } - } else { - /* just dark */ - if(cam->exposure_status == EXPOSURE_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_DARK; - cam->exposure_count = 1; - } - } - } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { - /* light */ - if(old_exposure <= VERY_LOW_EXP) { - /* very light */ - if(cam->exposure_status == EXPOSURE_VERY_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_LIGHT; - cam->exposure_count = 1; - } - } else { - /* just light */ - if(cam->exposure_status == EXPOSURE_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_LIGHT; - cam->exposure_count = 1; - } - } - } else { - /* not dark or light */ - cam->exposure_status = EXPOSURE_NORMAL; - } - } - - framerate = cam->fps; - if(framerate > 30 || framerate < 1) - framerate = 1; - - if(!cam->params.flickerControl.disabled) { - /* Flicker control on */ - if((cam->exposure_status == EXPOSURE_VERY_DARK || - cam->exposure_status == EXPOSURE_DARK) && - cam->exposure_count >= DARK_TIME*framerate && - cam->params.sensorFps.divisor < 3) { - - /* dark for too long */ - ++cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - cam->params.flickerControl.coarseJump = - flicker_jumps[cam->mainsFreq] - [cam->params.sensorFps.baserate] - [cam->params.sensorFps.divisor]; - cam->cmd_queue |= COMMAND_SETFLICKERCTRL; - - new_exposure = cam->params.flickerControl.coarseJump-1; - while(new_exposure < old_exposure/2) - new_exposure += cam->params.flickerControl.coarseJump; - cam->params.exposure.coarseExpLo = new_exposure & 0xff; - cam->params.exposure.coarseExpHi = new_exposure >> 8; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically decreasing sensor_fps\n"); - - } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || - cam->exposure_status == EXPOSURE_LIGHT) && - cam->exposure_count >= LIGHT_TIME*framerate && - cam->params.sensorFps.divisor > 0) { - - /* light for too long */ - int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ; - - --cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - cam->params.flickerControl.coarseJump = - flicker_jumps[cam->mainsFreq] - [cam->params.sensorFps.baserate] - [cam->params.sensorFps.divisor]; - cam->cmd_queue |= COMMAND_SETFLICKERCTRL; - - new_exposure = cam->params.flickerControl.coarseJump-1; - while(new_exposure < 2*old_exposure && - new_exposure+ - cam->params.flickerControl.coarseJump < max_exp) - new_exposure += cam->params.flickerControl.coarseJump; - cam->params.exposure.coarseExpLo = new_exposure & 0xff; - cam->params.exposure.coarseExpHi = new_exposure >> 8; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically increasing sensor_fps\n"); - } - } else { - /* Flicker control off */ - if((cam->exposure_status == EXPOSURE_VERY_DARK || - cam->exposure_status == EXPOSURE_DARK) && - cam->exposure_count >= DARK_TIME*framerate && - cam->params.sensorFps.divisor < 3) { - - /* dark for too long */ - ++cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - if(cam->params.exposure.gain > 0) { - --cam->params.exposure.gain; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - } - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically decreasing sensor_fps\n"); - - } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || - cam->exposure_status == EXPOSURE_LIGHT) && - cam->exposure_count >= LIGHT_TIME*framerate && - cam->params.sensorFps.divisor > 0) { - - /* light for too long */ - --cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - if(cam->params.exposure.gain < - cam->params.exposure.gainMode-1) { - ++cam->params.exposure.gain; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - } - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically increasing sensor_fps\n"); - } - } - mutex_unlock(&cam->param_lock); -} - -/*-----------------------------------------------------------------*/ -/* if flicker is switched off, this function switches it back on.It checks, - however, that conditions are suitable before restarting it. - This should only be called for firmware version 1.2. - - It also adjust the colour balance when an exposure step is detected - as - long as flicker is running -*/ -static void restart_flicker(struct cam_data *cam) -{ - int cam_exposure, old_exp; - if(!FIRMWARE_VERSION(1,2)) - return; - mutex_lock(&cam->param_lock); - if(cam->params.flickerControl.flickerMode == 0 || - cam->raw_image[39] == 0) { - mutex_unlock(&cam->param_lock); - return; - } - cam_exposure = cam->raw_image[39]*2; - old_exp = cam->params.exposure.coarseExpLo + - cam->params.exposure.coarseExpHi*256; - /* - see how far away camera exposure is from a valid - flicker exposure value - */ - cam_exposure %= cam->params.flickerControl.coarseJump; - if(!cam->params.flickerControl.disabled && - cam_exposure <= cam->params.flickerControl.coarseJump - 3) { - /* Flicker control auto-disabled */ - cam->params.flickerControl.disabled = 1; - } - - if(cam->params.flickerControl.disabled && - cam->params.flickerControl.flickerMode && - old_exp > cam->params.flickerControl.coarseJump + - ROUND_UP_EXP_FOR_FLICKER) { - /* exposure is now high enough to switch - flicker control back on */ - set_flicker(&cam->params, &cam->cmd_queue, 1); - if((cam->cmd_queue & COMMAND_SETEXPOSURE) && - cam->params.exposure.expMode == 2) - cam->exposure_status = EXPOSURE_NORMAL; - - } - mutex_unlock(&cam->param_lock); -} -#undef FIRMWARE_VERSION - -static int clear_stall(struct cam_data *cam) -{ - /* FIXME: Does this actually work? */ - LOG("Clearing stall\n"); - - cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0); - do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); - return cam->params.status.streamState != STREAM_PAUSED; -} - -/* kernel thread function to read image from camera */ -static int fetch_frame(void *data) -{ - int image_size, retry; - struct cam_data *cam = (struct cam_data *)data; - unsigned long oldjif, rate, diff; - - /* Allow up to two bad images in a row to be read and - * ignored before an error is reported */ - for (retry = 0; retry < 3; ++retry) { - if (retry) - DBG("retry=%d\n", retry); - - if (!cam->ops) - continue; - - /* load first frame always uncompressed */ - if (cam->first_frame && - cam->params.compression.mode != CPIA_COMPRESSION_NONE) { - do_command(cam, CPIA_COMMAND_SetCompression, - CPIA_COMPRESSION_NONE, - NO_DECIMATION, 0, 0); - /* Trial & error - Discarding a frame prevents the - first frame from having an error in the data. */ - do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); - } - - /* init camera upload */ - if (do_command(cam, CPIA_COMMAND_GrabFrame, 0, - cam->params.streamStartLine, 0, 0)) - continue; - - if (cam->ops->wait_for_stream_ready) { - /* loop until image ready */ - int count = 0; - do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); - while (cam->params.status.streamState != STREAM_READY) { - if(++count > READY_TIMEOUT) - break; - if(cam->params.status.streamState == - STREAM_PAUSED) { - /* Bad news */ - if(!clear_stall(cam)) - return -EIO; - } - - cond_resched(); - - /* sleep for 10 ms, hopefully ;) */ - msleep_interruptible(10); - if (signal_pending(current)) - return -EINTR; - - do_command(cam, CPIA_COMMAND_GetCameraStatus, - 0, 0, 0, 0); - } - if(cam->params.status.streamState != STREAM_READY) { - continue; - } - } - - cond_resched(); - - /* grab image from camera */ - oldjif = jiffies; - image_size = cam->ops->streamRead(cam->lowlevel_data, - cam->raw_image, 0); - if (image_size <= 0) { - DBG("streamRead failed: %d\n", image_size); - continue; - } - - rate = image_size * HZ / 1024; - diff = jiffies-oldjif; - cam->transfer_rate = diff==0 ? rate : rate/diff; - /* diff==0 ? unlikely but possible */ - - /* Switch flicker control back on if it got turned off */ - restart_flicker(cam); - - /* If AEC is enabled, monitor the exposure and - adjust the sensor frame rate if needed */ - if(cam->params.exposure.expMode == 2) - monitor_exposure(cam); - - /* camera idle now so dispatch queued commands */ - dispatch_commands(cam); - - /* Update our knowledge of the camera state */ - do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); - do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); - do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); - - /* decompress and convert image to by copying it from - * raw_image to decompressed_frame - */ - - cond_resched(); - - cam->image_size = parse_picture(cam, image_size); - if (cam->image_size <= 0) { - DBG("parse_picture failed %d\n", cam->image_size); - if(cam->params.compression.mode != - CPIA_COMPRESSION_NONE) { - /* Compression may not work right if we - had a bad frame, get the next one - uncompressed. */ - cam->first_frame = 1; - do_command(cam, CPIA_COMMAND_SetGrabMode, - CPIA_GRAB_SINGLE, 0, 0, 0); - /* FIXME: Trial & error - need up to 70ms for - the grab mode change to complete ? */ - msleep_interruptible(70); - if (signal_pending(current)) - return -EINTR; - } - } else - break; - } - - if (retry < 3) { - /* FIXME: this only works for double buffering */ - if (cam->frame[cam->curframe].state == FRAME_READY) { - memcpy(cam->frame[cam->curframe].data, - cam->decompressed_frame.data, - cam->decompressed_frame.count); - cam->frame[cam->curframe].state = FRAME_DONE; - } else - cam->decompressed_frame.state = FRAME_DONE; - - if (cam->first_frame) { - cam->first_frame = 0; - do_command(cam, CPIA_COMMAND_SetCompression, - cam->params.compression.mode, - cam->params.compression.decimation, 0, 0); - - /* Switch from single-grab to continuous grab */ - do_command(cam, CPIA_COMMAND_SetGrabMode, - CPIA_GRAB_CONTINUOUS, 0, 0, 0); - } - return 0; - } - return -EIO; -} - -static int capture_frame(struct cam_data *cam, struct video_mmap *vm) -{ - if (!cam->frame_buf) { - /* we do lazy allocation */ - int err; - if ((err = allocate_frame_buf(cam))) - return err; - } - - cam->curframe = vm->frame; - cam->frame[cam->curframe].state = FRAME_READY; - return fetch_frame(cam); -} - -static int goto_high_power(struct cam_data *cam) -{ - if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) - return -EIO; - msleep_interruptible(40); /* windows driver does it too */ - if(signal_pending(current)) - return -EINTR; - if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) - return -EIO; - if (cam->params.status.systemState == HI_POWER_STATE) { - DBG("camera now in HIGH power state\n"); - return 0; - } - printstatus(cam); - return -EIO; -} - -static int goto_low_power(struct cam_data *cam) -{ - if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) - return -1; - if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) - return -1; - if (cam->params.status.systemState == LO_POWER_STATE) { - DBG("camera now in LOW power state\n"); - return 0; - } - printstatus(cam); - return -1; -} - -static void save_camera_state(struct cam_data *cam) -{ - if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE)) - do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); - if(!(cam->cmd_queue & COMMAND_SETEXPOSURE)) - do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); - - DBG("%d/%d/%d/%d/%d/%d/%d/%d\n", - cam->params.exposure.gain, - cam->params.exposure.fineExp, - cam->params.exposure.coarseExpLo, - cam->params.exposure.coarseExpHi, - cam->params.exposure.redComp, - cam->params.exposure.green1Comp, - cam->params.exposure.green2Comp, - cam->params.exposure.blueComp); - DBG("%d/%d/%d\n", - cam->params.colourBalance.redGain, - cam->params.colourBalance.greenGain, - cam->params.colourBalance.blueGain); -} - -static int set_camera_state(struct cam_data *cam) -{ - cam->cmd_queue = COMMAND_SETCOMPRESSION | - COMMAND_SETCOMPRESSIONTARGET | - COMMAND_SETCOLOURPARAMS | - COMMAND_SETFORMAT | - COMMAND_SETYUVTHRESH | - COMMAND_SETECPTIMING | - COMMAND_SETCOMPRESSIONPARAMS | - COMMAND_SETEXPOSURE | - COMMAND_SETCOLOURBALANCE | - COMMAND_SETSENSORFPS | - COMMAND_SETAPCOR | - COMMAND_SETFLICKERCTRL | - COMMAND_SETVLOFFSET; - - do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0); - dispatch_commands(cam); - - /* Wait 6 frames for the sensor to get all settings and - AEC/ACB to settle */ - msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) * - (1 << cam->params.sensorFps.divisor) + 10); - - if(signal_pending(current)) - return -EINTR; - - save_camera_state(cam); - - return 0; -} - -static void get_version_information(struct cam_data *cam) -{ - /* GetCPIAVersion */ - do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0); - - /* GetPnPID */ - do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0); -} - -/* initialize camera */ -static int reset_camera(struct cam_data *cam) -{ - int err; - /* Start the camera in low power mode */ - if (goto_low_power(cam)) { - if (cam->params.status.systemState != WARM_BOOT_STATE) - return -ENODEV; - - /* FIXME: this is just dirty trial and error */ - err = goto_high_power(cam); - if(err) - return err; - do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); - if (goto_low_power(cam)) - return -ENODEV; - } - - /* procedure described in developer's guide p3-28 */ - - /* Check the firmware version. */ - cam->params.version.firmwareVersion = 0; - get_version_information(cam); - if (cam->params.version.firmwareVersion != 1) - return -ENODEV; - - /* A bug in firmware 1-02 limits gainMode to 2 */ - if(cam->params.version.firmwareRevision <= 2 && - cam->params.exposure.gainMode > 2) { - cam->params.exposure.gainMode = 2; - } - - /* set QX3 detected flag */ - cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 && - cam->params.pnpID.product == 0x0001); - - /* The fatal error checking should be done after - * the camera powers up (developer's guide p 3-38) */ - - /* Set streamState before transition to high power to avoid bug - * in firmware 1-02 */ - do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0, - STREAM_NOT_READY, 0); - - /* GotoHiPower */ - err = goto_high_power(cam); - if (err) - return err; - - /* Check the camera status */ - if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) - return -EIO; - - if (cam->params.status.fatalError) { - DBG("fatal_error: %#04x\n", - cam->params.status.fatalError); - DBG("vp_status: %#04x\n", - cam->params.status.vpStatus); - if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) { - /* Fatal error in camera */ - return -EIO; - } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) { - /* Firmware 1-02 may do this for parallel port cameras, - * just clear the flags (developer's guide p 3-38) */ - do_command(cam, CPIA_COMMAND_ModifyCameraStatus, - FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0); - } - } - - /* Check the camera status again */ - if (cam->params.status.fatalError) { - if (cam->params.status.fatalError) - return -EIO; - } - - /* VPVersion can't be retrieved before the camera is in HiPower, - * so get it here instead of in get_version_information. */ - do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0); - - /* set camera to a known state */ - return set_camera_state(cam); -} - -static void put_cam(struct cpia_camera_ops* ops) -{ - module_put(ops->owner); -} - -/* ------------------------- V4L interface --------------------- */ -static int cpia_open(struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct cam_data *cam = video_get_drvdata(dev); - int err; - - if (!cam) { - DBG("Internal error, cam_data not found!\n"); - return -ENODEV; - } - - if (cam->open_count > 0) { - DBG("Camera already open\n"); - return -EBUSY; - } - - if (!try_module_get(cam->ops->owner)) - return -ENODEV; - - mutex_lock(&cam->busy_lock); - err = -ENOMEM; - if (!cam->raw_image) { - cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE); - if (!cam->raw_image) - goto oops; - } - - if (!cam->decompressed_frame.data) { - cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE); - if (!cam->decompressed_frame.data) - goto oops; - } - - /* open cpia */ - err = -ENODEV; - if (cam->ops->open(cam->lowlevel_data)) - goto oops; - - /* reset the camera */ - if ((err = reset_camera(cam)) != 0) { - cam->ops->close(cam->lowlevel_data); - goto oops; - } - - err = -EINTR; - if(signal_pending(current)) - goto oops; - - /* Set ownership of /proc/cpia/videoX to current user */ - if(cam->proc_entry) - cam->proc_entry->uid = current_uid(); - - /* set mark for loading first frame uncompressed */ - cam->first_frame = 1; - - /* init it to something */ - cam->mmap_kludge = 0; - - ++cam->open_count; - file->private_data = dev; - mutex_unlock(&cam->busy_lock); - return 0; - - oops: - if (cam->decompressed_frame.data) { - rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data = NULL; - } - if (cam->raw_image) { - rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image = NULL; - } - mutex_unlock(&cam->busy_lock); - put_cam(cam->ops); - return err; -} - -static int cpia_close(struct file *file) -{ - struct video_device *dev = file->private_data; - struct cam_data *cam = video_get_drvdata(dev); - - if (cam->ops) { - /* Return ownership of /proc/cpia/videoX to root */ - if(cam->proc_entry) - cam->proc_entry->uid = 0; - - /* save camera state for later open (developers guide ch 3.5.3) */ - save_camera_state(cam); - - /* GotoLoPower */ - goto_low_power(cam); - - /* Update the camera status */ - do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); - - /* cleanup internal state stuff */ - free_frames(cam->frame); - - /* close cpia */ - cam->ops->close(cam->lowlevel_data); - - put_cam(cam->ops); - } - - if (--cam->open_count == 0) { - /* clean up capture-buffers */ - if (cam->raw_image) { - rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image = NULL; - } - - if (cam->decompressed_frame.data) { - rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data = NULL; - } - - if (cam->frame_buf) - free_frame_buf(cam); - - if (!cam->ops) - kfree(cam); - } - file->private_data = NULL; - - return 0; -} - -static ssize_t cpia_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct video_device *dev = file->private_data; - struct cam_data *cam = video_get_drvdata(dev); - int err; - - /* make this _really_ smp and multithread-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -EINTR; - - if (!buf) { - DBG("buf NULL\n"); - mutex_unlock(&cam->busy_lock); - return -EINVAL; - } - - if (!count) { - DBG("count 0\n"); - mutex_unlock(&cam->busy_lock); - return 0; - } - - if (!cam->ops) { - DBG("ops NULL\n"); - mutex_unlock(&cam->busy_lock); - return -ENODEV; - } - - /* upload frame */ - cam->decompressed_frame.state = FRAME_READY; - cam->mmap_kludge=0; - if((err = fetch_frame(cam)) != 0) { - DBG("ERROR from fetch_frame: %d\n", err); - mutex_unlock(&cam->busy_lock); - return err; - } - cam->decompressed_frame.state = FRAME_UNUSED; - - /* copy data to user space */ - if (cam->decompressed_frame.count > count) { - DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count, - (unsigned long) count); - mutex_unlock(&cam->busy_lock); - return -EFAULT; - } - if (copy_to_user(buf, cam->decompressed_frame.data, - cam->decompressed_frame.count)) { - DBG("copy_to_user failed\n"); - mutex_unlock(&cam->busy_lock); - return -EFAULT; - } - - mutex_unlock(&cam->busy_lock); - return cam->decompressed_frame.count; -} - -static long cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct video_device *dev = file->private_data; - struct cam_data *cam = video_get_drvdata(dev); - int retval = 0; - - if (!cam || !cam->ops) - return -ENODEV; - - /* make this _really_ smp-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -EINTR; - - /* DBG("cpia_ioctl: %u\n", cmd); */ - - switch (cmd) { - /* query capabilities */ - case VIDIOCGCAP: - { - struct video_capability *b = arg; - - DBG("VIDIOCGCAP\n"); - strcpy(b->name, "CPiA Camera"); - b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; - b->channels = 1; - b->audios = 0; - b->maxwidth = 352; /* VIDEOSIZE_CIF */ - b->maxheight = 288; - b->minwidth = 48; /* VIDEOSIZE_48_48 */ - b->minheight = 48; - break; - } - - /* get/set video source - we are a camera and nothing else */ - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - - DBG("VIDIOCGCHAN\n"); - if (v->channel != 0) { - retval = -EINVAL; - break; - } - - v->channel = 0; - strcpy(v->name, "Camera"); - v->tuners = 0; - v->flags = 0; - v->type = VIDEO_TYPE_CAMERA; - v->norm = 0; - break; - } - - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - - DBG("VIDIOCSCHAN\n"); - if (v->channel != 0) - retval = -EINVAL; - break; - } - - /* image properties */ - case VIDIOCGPICT: - { - struct video_picture *pic = arg; - DBG("VIDIOCGPICT\n"); - *pic = cam->vp; - break; - } - - case VIDIOCSPICT: - { - struct video_picture *vp = arg; - - DBG("VIDIOCSPICT\n"); - - /* check validity */ - DBG("palette: %d\n", vp->palette); - DBG("depth: %d\n", vp->depth); - if (!valid_mode(vp->palette, vp->depth)) { - retval = -EINVAL; - break; - } - - mutex_lock(&cam->param_lock); - /* brightness, colour, contrast need no check 0-65535 */ - cam->vp = *vp; - /* update cam->params.colourParams */ - cam->params.colourParams.brightness = vp->brightness*100/65535; - cam->params.colourParams.contrast = vp->contrast*100/65535; - cam->params.colourParams.saturation = vp->colour*100/65535; - /* contrast is in steps of 8, so round */ - cam->params.colourParams.contrast = - ((cam->params.colourParams.contrast + 3) / 8) * 8; - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2 && - cam->params.colourParams.contrast > 80) { - /* 1-02 firmware limits contrast to 80 */ - cam->params.colourParams.contrast = 80; - } - - /* Adjust flicker control if necessary */ - if(cam->params.flickerControl.allowableOverExposure < 0) - cam->params.flickerControl.allowableOverExposure = - -find_over_exposure(cam->params.colourParams.brightness); - if(cam->params.flickerControl.flickerMode != 0) - cam->cmd_queue |= COMMAND_SETFLICKERCTRL; - - - /* queue command to update camera */ - cam->cmd_queue |= COMMAND_SETCOLOURPARAMS; - mutex_unlock(&cam->param_lock); - DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n", - vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour, - vp->contrast); - break; - } - - /* get/set capture window */ - case VIDIOCGWIN: - { - struct video_window *vw = arg; - DBG("VIDIOCGWIN\n"); - - *vw = cam->vw; - break; - } - - case VIDIOCSWIN: - { - /* copy_from_user, check validity, copy to internal structure */ - struct video_window *vw = arg; - DBG("VIDIOCSWIN\n"); - - if (vw->clipcount != 0) { /* clipping not supported */ - retval = -EINVAL; - break; - } - if (vw->clips != NULL) { /* clipping not supported */ - retval = -EINVAL; - break; - } - - /* we set the video window to something smaller or equal to what - * is requested by the user??? - */ - mutex_lock(&cam->param_lock); - if (vw->width != cam->vw.width || vw->height != cam->vw.height) { - int video_size = match_videosize(vw->width, vw->height); - - if (video_size < 0) { - retval = -EINVAL; - mutex_unlock(&cam->param_lock); - break; - } - cam->video_size = video_size; - - /* video size is changing, reset the subcapture area */ - memset(&cam->vc, 0, sizeof(cam->vc)); - - set_vw_size(cam); - DBG("%d / %d\n", cam->vw.width, cam->vw.height); - cam->cmd_queue |= COMMAND_SETFORMAT; - } - - mutex_unlock(&cam->param_lock); - - /* setformat ignored by camera during streaming, - * so stop/dispatch/start */ - if (cam->cmd_queue & COMMAND_SETFORMAT) { - DBG("\n"); - dispatch_commands(cam); - } - DBG("%d/%d:%d\n", cam->video_size, - cam->vw.width, cam->vw.height); - break; - } - - /* mmap interface */ - case VIDIOCGMBUF: - { - struct video_mbuf *vm = arg; - int i; - - DBG("VIDIOCGMBUF\n"); - memset(vm, 0, sizeof(*vm)); - vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM; - vm->frames = FRAME_NUM; - for (i = 0; i < FRAME_NUM; i++) - vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i; - break; - } - - case VIDIOCMCAPTURE: - { - struct video_mmap *vm = arg; - int video_size; - - DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame, - vm->width, vm->height); - if (vm->frame<0||vm->frame>=FRAME_NUM) { - retval = -EINVAL; - break; - } - - /* set video format */ - cam->vp.palette = vm->format; - switch(vm->format) { - case VIDEO_PALETTE_GREY: - cam->vp.depth=8; - break; - case VIDEO_PALETTE_RGB555: - case VIDEO_PALETTE_RGB565: - case VIDEO_PALETTE_YUV422: - case VIDEO_PALETTE_YUYV: - case VIDEO_PALETTE_UYVY: - cam->vp.depth = 16; - break; - case VIDEO_PALETTE_RGB24: - cam->vp.depth = 24; - break; - case VIDEO_PALETTE_RGB32: - cam->vp.depth = 32; - break; - default: - retval = -EINVAL; - break; - } - if (retval) - break; - - /* set video size */ - video_size = match_videosize(vm->width, vm->height); - if (video_size < 0) { - retval = -EINVAL; - break; - } - if (video_size != cam->video_size) { - cam->video_size = video_size; - - /* video size is changing, reset the subcapture area */ - memset(&cam->vc, 0, sizeof(cam->vc)); - - set_vw_size(cam); - cam->cmd_queue |= COMMAND_SETFORMAT; - dispatch_commands(cam); - } - /* according to v4l-spec we must start streaming here */ - cam->mmap_kludge = 1; - retval = capture_frame(cam, vm); - - break; - } - - case VIDIOCSYNC: - { - int *frame = arg; - - //DBG("VIDIOCSYNC: %d\n", *frame); - - if (*frame<0 || *frame >= FRAME_NUM) { - retval = -EINVAL; - break; - } - - switch (cam->frame[*frame].state) { - case FRAME_UNUSED: - case FRAME_READY: - case FRAME_GRABBING: - DBG("sync to unused frame %d\n", *frame); - retval = -EINVAL; - break; - - case FRAME_DONE: - cam->frame[*frame].state = FRAME_UNUSED; - //DBG("VIDIOCSYNC: %d synced\n", *frame); - break; - } - if (retval == -EINTR) { - /* FIXME - xawtv does not handle this nice */ - retval = 0; - } - break; - } - - case VIDIOCGCAPTURE: - { - struct video_capture *vc = arg; - - DBG("VIDIOCGCAPTURE\n"); - - *vc = cam->vc; - - break; - } - - case VIDIOCSCAPTURE: - { - struct video_capture *vc = arg; - - DBG("VIDIOCSCAPTURE\n"); - - if (vc->decimation != 0) { /* How should this be used? */ - retval = -EINVAL; - break; - } - if (vc->flags != 0) { /* Even/odd grab not supported */ - retval = -EINVAL; - break; - } - - /* Clip to the resolution we can set for the ROI - (every 8 columns and 4 rows) */ - vc->x = vc->x & ~(__u32)7; - vc->y = vc->y & ~(__u32)3; - vc->width = vc->width & ~(__u32)7; - vc->height = vc->height & ~(__u32)3; - - if(vc->width == 0 || vc->height == 0 || - vc->x + vc->width > cam->vw.width || - vc->y + vc->height > cam->vw.height) { - retval = -EINVAL; - break; - } - - DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height); - - mutex_lock(&cam->param_lock); - - cam->vc.x = vc->x; - cam->vc.y = vc->y; - cam->vc.width = vc->width; - cam->vc.height = vc->height; - - set_vw_size(cam); - cam->cmd_queue |= COMMAND_SETFORMAT; - - mutex_unlock(&cam->param_lock); - - /* setformat ignored by camera during streaming, - * so stop/dispatch/start */ - dispatch_commands(cam); - break; - } - - case VIDIOCGUNIT: - { - struct video_unit *vu = arg; - - DBG("VIDIOCGUNIT\n"); - - vu->video = cam->vdev.minor; - vu->vbi = VIDEO_NO_UNIT; - vu->radio = VIDEO_NO_UNIT; - vu->audio = VIDEO_NO_UNIT; - vu->teletext = VIDEO_NO_UNIT; - - break; - } - - - /* pointless to implement overlay with this camera */ - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCKEY: - /* tuner interface - we have none */ - case VIDIOCGTUNER: - case VIDIOCSTUNER: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - /* audio interface - we have none */ - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - retval = -EINVAL; - break; - default: - retval = -ENOIOCTLCMD; - break; - } - - mutex_unlock(&cam->busy_lock); - return retval; -} - -static long cpia_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, cpia_do_ioctl); -} - - -/* FIXME */ -static int cpia_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *dev = file->private_data; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end - vma->vm_start; - unsigned long page, pos; - struct cam_data *cam = video_get_drvdata(dev); - int retval; - - if (!cam || !cam->ops) - return -ENODEV; - - DBG("cpia_mmap: %ld\n", size); - - if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE) - return -EINVAL; - - /* make this _really_ smp-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -EINTR; - - if (!cam->frame_buf) { /* we do lazy allocation */ - if ((retval = allocate_frame_buf(cam))) { - mutex_unlock(&cam->busy_lock); - return retval; - } - } - - pos = (unsigned long)(cam->frame_buf); - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - mutex_unlock(&cam->busy_lock); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - DBG("cpia_mmap: %ld\n", size); - mutex_unlock(&cam->busy_lock); - - return 0; -} - -static const struct v4l2_file_operations cpia_fops = { - .owner = THIS_MODULE, - .open = cpia_open, - .release = cpia_close, - .read = cpia_read, - .mmap = cpia_mmap, - .ioctl = cpia_ioctl, -}; - -static struct video_device cpia_template = { - .name = "CPiA Camera", - .fops = &cpia_fops, - .release = video_device_release_empty, -}; - -/* initialise cam_data structure */ -static void reset_camera_struct(struct cam_data *cam) -{ - /* The following parameter values are the defaults from - * "Software Developer's Guide for CPiA Cameras". Any changes - * to the defaults are noted in comments. */ - cam->params.colourParams.brightness = 50; - cam->params.colourParams.contrast = 48; - cam->params.colourParams.saturation = 50; - cam->params.exposure.gainMode = 4; - cam->params.exposure.expMode = 2; /* AEC */ - cam->params.exposure.compMode = 1; - cam->params.exposure.centreWeight = 1; - cam->params.exposure.gain = 0; - cam->params.exposure.fineExp = 0; - cam->params.exposure.coarseExpLo = 185; - cam->params.exposure.coarseExpHi = 0; - cam->params.exposure.redComp = COMP_RED; - cam->params.exposure.green1Comp = COMP_GREEN1; - cam->params.exposure.green2Comp = COMP_GREEN2; - cam->params.exposure.blueComp = COMP_BLUE; - cam->params.colourBalance.balanceMode = 2; /* ACB */ - cam->params.colourBalance.redGain = 32; - cam->params.colourBalance.greenGain = 6; - cam->params.colourBalance.blueGain = 92; - cam->params.apcor.gain1 = 0x18; - cam->params.apcor.gain2 = 0x16; - cam->params.apcor.gain4 = 0x24; - cam->params.apcor.gain8 = 0x34; - cam->params.flickerControl.flickerMode = 0; - cam->params.flickerControl.disabled = 1; - - cam->params.flickerControl.coarseJump = - flicker_jumps[cam->mainsFreq] - [cam->params.sensorFps.baserate] - [cam->params.sensorFps.divisor]; - cam->params.flickerControl.allowableOverExposure = - -find_over_exposure(cam->params.colourParams.brightness); - cam->params.vlOffset.gain1 = 20; - cam->params.vlOffset.gain2 = 24; - cam->params.vlOffset.gain4 = 26; - cam->params.vlOffset.gain8 = 26; - cam->params.compressionParams.hysteresis = 3; - cam->params.compressionParams.threshMax = 11; - cam->params.compressionParams.smallStep = 1; - cam->params.compressionParams.largeStep = 3; - cam->params.compressionParams.decimationHysteresis = 2; - cam->params.compressionParams.frDiffStepThresh = 5; - cam->params.compressionParams.qDiffStepThresh = 3; - cam->params.compressionParams.decimationThreshMod = 2; - /* End of default values from Software Developer's Guide */ - - cam->transfer_rate = 0; - cam->exposure_status = EXPOSURE_NORMAL; - - /* Set Sensor FPS to 15fps. This seems better than 30fps - * for indoor lighting. */ - cam->params.sensorFps.divisor = 1; - cam->params.sensorFps.baserate = 1; - - cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */ - cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */ - - cam->params.format.subSample = SUBSAMPLE_422; - cam->params.format.yuvOrder = YUVORDER_YUYV; - - cam->params.compression.mode = CPIA_COMPRESSION_AUTO; - cam->params.compressionTarget.frTargeting = - CPIA_COMPRESSION_TARGET_QUALITY; - cam->params.compressionTarget.targetFR = 15; /* From windows driver */ - cam->params.compressionTarget.targetQ = 5; /* From windows driver */ - - cam->params.qx3.qx3_detected = 0; - cam->params.qx3.toplight = 0; - cam->params.qx3.bottomlight = 0; - cam->params.qx3.button = 0; - cam->params.qx3.cradled = 0; - - cam->video_size = VIDEOSIZE_CIF; - - cam->vp.colour = 32768; /* 50% */ - cam->vp.hue = 32768; /* 50% */ - cam->vp.brightness = 32768; /* 50% */ - cam->vp.contrast = 32768; /* 50% */ - cam->vp.whiteness = 0; /* not used -> grayscale only */ - cam->vp.depth = 24; /* to be set by user */ - cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */ - - cam->vc.x = 0; - cam->vc.y = 0; - cam->vc.width = 0; - cam->vc.height = 0; - - cam->vw.x = 0; - cam->vw.y = 0; - set_vw_size(cam); - cam->vw.chromakey = 0; - cam->vw.flags = 0; - cam->vw.clipcount = 0; - cam->vw.clips = NULL; - - cam->cmd_queue = COMMAND_NONE; - cam->first_frame = 1; - - return; -} - -/* initialize cam_data structure */ -static void init_camera_struct(struct cam_data *cam, - struct cpia_camera_ops *ops ) -{ - int i; - - /* Default everything to 0 */ - memset(cam, 0, sizeof(struct cam_data)); - - cam->ops = ops; - mutex_init(&cam->param_lock); - mutex_init(&cam->busy_lock); - - reset_camera_struct(cam); - - cam->proc_entry = NULL; - - memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template)); - video_set_drvdata(&cam->vdev, cam); - - cam->curframe = 0; - for (i = 0; i < FRAME_NUM; i++) { - cam->frame[i].width = 0; - cam->frame[i].height = 0; - cam->frame[i].state = FRAME_UNUSED; - cam->frame[i].data = NULL; - } - cam->decompressed_frame.width = 0; - cam->decompressed_frame.height = 0; - cam->decompressed_frame.state = FRAME_UNUSED; - cam->decompressed_frame.data = NULL; -} - -struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel) -{ - struct cam_data *camera; - - if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) - return NULL; - - - init_camera_struct( camera, ops ); - camera->lowlevel_data = lowlevel; - - /* register v4l device */ - if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - kfree(camera); - printk(KERN_DEBUG "video_register_device failed\n"); - return NULL; - } - - /* get version information from camera: open/reset/close */ - - /* open cpia */ - if (camera->ops->open(camera->lowlevel_data)) - return camera; - - /* reset the camera */ - if (reset_camera(camera) != 0) { - camera->ops->close(camera->lowlevel_data); - return camera; - } - - /* close cpia */ - camera->ops->close(camera->lowlevel_data); - -#ifdef CONFIG_PROC_FS - create_proc_cpia_cam(camera); -#endif - - printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n", - camera->params.version.firmwareVersion, - camera->params.version.firmwareRevision, - camera->params.version.vcVersion, - camera->params.version.vcRevision); - printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n", - camera->params.pnpID.vendor, - camera->params.pnpID.product, - camera->params.pnpID.deviceRevision); - printk(KERN_INFO " VP-Version: %d.%d %04x\n", - camera->params.vpVersion.vpVersion, - camera->params.vpVersion.vpRevision, - camera->params.vpVersion.cameraHeadID); - - return camera; -} - -void cpia_unregister_camera(struct cam_data *cam) -{ - DBG("unregistering video\n"); - video_unregister_device(&cam->vdev); - if (cam->open_count) { - put_cam(cam->ops); - DBG("camera open -- setting ops to NULL\n"); - cam->ops = NULL; - } - -#ifdef CONFIG_PROC_FS - DBG("destroying /proc/cpia/%s\n", video_device_node_name(&cam->vdev)); - destroy_proc_cpia_cam(cam); -#endif - if (!cam->open_count) { - DBG("freeing camera\n"); - kfree(cam); - } -} - -static int __init cpia_init(void) -{ - printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, - CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); - - printk(KERN_WARNING "Since in-kernel colorspace conversion is not " - "allowed, it is disabled by default now. Users should fix the " - "applications in case they don't work without conversion " - "reenabled by setting the 'colorspace_conv' module " - "parameter to 1\n"); - -#ifdef CONFIG_PROC_FS - proc_cpia_create(); -#endif - - return 0; -} - -static void __exit cpia_exit(void) -{ -#ifdef CONFIG_PROC_FS - proc_cpia_destroy(); -#endif -} - -module_init(cpia_init); -module_exit(cpia_exit); - -/* Exported symbols for modules. */ - -EXPORT_SYMBOL(cpia_register_camera); -EXPORT_SYMBOL(cpia_unregister_camera); diff --git a/drivers/media/video/cpia.h b/drivers/media/video/cpia.h deleted file mode 100644 index 8f0cfee4b8a1..000000000000 --- a/drivers/media/video/cpia.h +++ /dev/null @@ -1,432 +0,0 @@ -#ifndef cpia_h -#define cpia_h - -/* - * CPiA Parallel Port Video4Linux driver - * - * Supports CPiA based parallel port Video Camera's. - * - * (C) Copyright 1999 Bas Huisman, - * Peter Pregler, - * Scott J. Bertin, - * VLSI Vision Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#define CPIA_MAJ_VER 1 -#define CPIA_MIN_VER 2 -#define CPIA_PATCH_VER 3 - -#define CPIA_PP_MAJ_VER CPIA_MAJ_VER -#define CPIA_PP_MIN_VER CPIA_MIN_VER -#define CPIA_PP_PATCH_VER CPIA_PATCH_VER - -#define CPIA_USB_MAJ_VER CPIA_MAJ_VER -#define CPIA_USB_MIN_VER CPIA_MIN_VER -#define CPIA_USB_PATCH_VER CPIA_PATCH_VER - -#define CPIA_MAX_FRAME_SIZE_UNALIGNED (352 * 288 * 4) /* CIF at RGB32 */ -#define CPIA_MAX_FRAME_SIZE ((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */ - -#ifdef __KERNEL__ - -#include -#include -#include -#include -#include -#include - -struct cpia_camera_ops -{ - /* open sets privdata to point to structure for this camera. - * Returns negative value on error, otherwise 0. - */ - int (*open)(void *privdata); - - /* Registers callback function cb to be called with cbdata - * when an image is ready. If cb is NULL, only single image grabs - * should be used. cb should immediately call streamRead to read - * the data or data may be lost. Returns negative value on error, - * otherwise 0. - */ - int (*registerCallback)(void *privdata, void (*cb)(void *cbdata), - void *cbdata); - - /* transferCmd sends commands to the camera. command MUST point to - * an 8 byte buffer in kernel space. data can be NULL if no extra - * data is needed. The size of the data is given by the last 2 - * bytes of command. data must also point to memory in kernel space. - * Returns negative value on error, otherwise 0. - */ - int (*transferCmd)(void *privdata, u8 *command, u8 *data); - - /* streamStart initiates stream capture mode. - * Returns negative value on error, otherwise 0. - */ - int (*streamStart)(void *privdata); - - /* streamStop terminates stream capture mode. - * Returns negative value on error, otherwise 0. - */ - int (*streamStop)(void *privdata); - - /* streamRead reads a frame from the camera. buffer points to a - * buffer large enough to hold a complete frame in kernel space. - * noblock indicates if this should be a non blocking read. - * Returns the number of bytes read, or negative value on error. - */ - int (*streamRead)(void *privdata, u8 *buffer, int noblock); - - /* close disables the device until open() is called again. - * Returns negative value on error, otherwise 0. - */ - int (*close)(void *privdata); - - /* If wait_for_stream_ready is non-zero, wait until the streamState - * is STREAM_READY before calling streamRead. - */ - int wait_for_stream_ready; - - /* - * Used to maintain lowlevel module usage counts - */ - struct module *owner; -}; - -struct cpia_frame { - u8 *data; - int count; - int width; - int height; - volatile int state; -}; - -struct cam_params { - struct { - u8 firmwareVersion; - u8 firmwareRevision; - u8 vcVersion; - u8 vcRevision; - } version; - struct { - u16 vendor; - u16 product; - u16 deviceRevision; - } pnpID; - struct { - u8 vpVersion; - u8 vpRevision; - u16 cameraHeadID; - } vpVersion; - struct { - u8 systemState; - u8 grabState; - u8 streamState; - u8 fatalError; - u8 cmdError; - u8 debugFlags; - u8 vpStatus; - u8 errorCode; - } status; - struct { - u8 brightness; - u8 contrast; - u8 saturation; - } colourParams; - struct { - u8 gainMode; - u8 expMode; - u8 compMode; - u8 centreWeight; - u8 gain; - u8 fineExp; - u8 coarseExpLo; - u8 coarseExpHi; - u8 redComp; - u8 green1Comp; - u8 green2Comp; - u8 blueComp; - } exposure; - struct { - u8 balanceMode; - u8 redGain; - u8 greenGain; - u8 blueGain; - } colourBalance; - struct { - u8 divisor; - u8 baserate; - } sensorFps; - struct { - u8 gain1; - u8 gain2; - u8 gain4; - u8 gain8; - } apcor; - struct { - u8 disabled; - u8 flickerMode; - u8 coarseJump; - int allowableOverExposure; - } flickerControl; - struct { - u8 gain1; - u8 gain2; - u8 gain4; - u8 gain8; - } vlOffset; - struct { - u8 mode; - u8 decimation; - } compression; - struct { - u8 frTargeting; - u8 targetFR; - u8 targetQ; - } compressionTarget; - struct { - u8 yThreshold; - u8 uvThreshold; - } yuvThreshold; - struct { - u8 hysteresis; - u8 threshMax; - u8 smallStep; - u8 largeStep; - u8 decimationHysteresis; - u8 frDiffStepThresh; - u8 qDiffStepThresh; - u8 decimationThreshMod; - } compressionParams; - struct { - u8 videoSize; /* CIF/QCIF */ - u8 subSample; - u8 yuvOrder; - } format; - struct { /* Intel QX3 specific data */ - u8 qx3_detected; /* a QX3 is present */ - u8 toplight; /* top light lit , R/W */ - u8 bottomlight; /* bottom light lit, R/W */ - u8 button; /* snapshot button pressed (R/O) */ - u8 cradled; /* microscope is in cradle (R/O) */ - } qx3; - struct { - u8 colStart; /* skip first 8*colStart pixels */ - u8 colEnd; /* finish at 8*colEnd pixels */ - u8 rowStart; /* skip first 4*rowStart lines */ - u8 rowEnd; /* finish at 4*rowEnd lines */ - } roi; - u8 ecpTiming; - u8 streamStartLine; -}; - -enum v4l_camstates { - CPIA_V4L_IDLE = 0, - CPIA_V4L_ERROR, - CPIA_V4L_COMMAND, - CPIA_V4L_GRABBING, - CPIA_V4L_STREAMING, - CPIA_V4L_STREAMING_PAUSED, -}; - -#define FRAME_NUM 2 /* double buffering for now */ - -struct cam_data { - struct list_head cam_data_list; - - struct mutex busy_lock; /* guard against SMP multithreading */ - struct cpia_camera_ops *ops; /* lowlevel driver operations */ - void *lowlevel_data; /* private data for lowlevel driver */ - u8 *raw_image; /* buffer for raw image data */ - struct cpia_frame decompressed_frame; - /* buffer to hold decompressed frame */ - int image_size; /* sizeof last decompressed image */ - int open_count; /* # of process that have camera open */ - - /* camera status */ - int fps; /* actual fps reported by the camera */ - int transfer_rate; /* transfer rate from camera in kB/s */ - u8 mainsFreq; /* for flicker control */ - - /* proc interface */ - struct mutex param_lock; /* params lock for this camera */ - struct cam_params params; /* camera settings */ - struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */ - - /* v4l */ - int video_size; /* VIDEO_SIZE_ */ - volatile enum v4l_camstates camstate; /* v4l layer status */ - struct video_device vdev; /* v4l videodev */ - struct video_picture vp; /* v4l camera settings */ - struct video_window vw; /* v4l capture area */ - struct video_capture vc; /* v4l subcapture area */ - - /* mmap interface */ - int curframe; /* the current frame to grab into */ - u8 *frame_buf; /* frame buffer data */ - struct cpia_frame frame[FRAME_NUM]; - /* FRAME_NUM-buffering, so we need a array */ - - int first_frame; - int mmap_kludge; /* 'wrong' byte order for mmap */ - volatile u32 cmd_queue; /* queued commands */ - int exposure_status; /* EXPOSURE_* */ - int exposure_count; /* number of frames at this status */ -}; - -/* cpia_register_camera is called by low level driver for each camera. - * A unique camera number is returned, or a negative value on error */ -struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel); - -/* cpia_unregister_camera is called by low level driver when a camera - * is removed. This must not fail. */ -void cpia_unregister_camera(struct cam_data *cam); - -/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI + - * one byte 16bit DMA alignment - */ -#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5) - -/* constant value's */ -#define MAGIC_0 0x19 -#define MAGIC_1 0x68 -#define DATA_IN 0xC0 -#define DATA_OUT 0x40 -#define VIDEOSIZE_QCIF 0 /* 176x144 */ -#define VIDEOSIZE_CIF 1 /* 352x288 */ -#define VIDEOSIZE_SIF 2 /* 320x240 */ -#define VIDEOSIZE_QSIF 3 /* 160x120 */ -#define VIDEOSIZE_48_48 4 /* where no one has gone before, iconsize! */ -#define VIDEOSIZE_64_48 5 -#define VIDEOSIZE_128_96 6 -#define VIDEOSIZE_160_120 VIDEOSIZE_QSIF -#define VIDEOSIZE_176_144 VIDEOSIZE_QCIF -#define VIDEOSIZE_192_144 7 -#define VIDEOSIZE_224_168 8 -#define VIDEOSIZE_256_192 9 -#define VIDEOSIZE_288_216 10 -#define VIDEOSIZE_320_240 VIDEOSIZE_SIF -#define VIDEOSIZE_352_288 VIDEOSIZE_CIF -#define VIDEOSIZE_88_72 11 /* quarter CIF */ -#define SUBSAMPLE_420 0 -#define SUBSAMPLE_422 1 -#define YUVORDER_YUYV 0 -#define YUVORDER_UYVY 1 -#define NOT_COMPRESSED 0 -#define COMPRESSED 1 -#define NO_DECIMATION 0 -#define DECIMATION_ENAB 1 -#define EOI 0xff /* End Of Image */ -#define EOL 0xfd /* End Of Line */ -#define FRAME_HEADER_SIZE 64 - -/* Image grab modes */ -#define CPIA_GRAB_SINGLE 0 -#define CPIA_GRAB_CONTINUOUS 1 - -/* Compression parameters */ -#define CPIA_COMPRESSION_NONE 0 -#define CPIA_COMPRESSION_AUTO 1 -#define CPIA_COMPRESSION_MANUAL 2 -#define CPIA_COMPRESSION_TARGET_QUALITY 0 -#define CPIA_COMPRESSION_TARGET_FRAMERATE 1 - -/* Return offsets for GetCameraState */ -#define SYSTEMSTATE 0 -#define GRABSTATE 1 -#define STREAMSTATE 2 -#define FATALERROR 3 -#define CMDERROR 4 -#define DEBUGFLAGS 5 -#define VPSTATUS 6 -#define ERRORCODE 7 - -/* SystemState */ -#define UNINITIALISED_STATE 0 -#define PASS_THROUGH_STATE 1 -#define LO_POWER_STATE 2 -#define HI_POWER_STATE 3 -#define WARM_BOOT_STATE 4 - -/* GrabState */ -#define GRAB_IDLE 0 -#define GRAB_ACTIVE 1 -#define GRAB_DONE 2 - -/* StreamState */ -#define STREAM_NOT_READY 0 -#define STREAM_READY 1 -#define STREAM_OPEN 2 -#define STREAM_PAUSED 3 -#define STREAM_FINISHED 4 - -/* Fatal Error, CmdError, and DebugFlags */ -#define CPIA_FLAG 1 -#define SYSTEM_FLAG 2 -#define INT_CTRL_FLAG 4 -#define PROCESS_FLAG 8 -#define COM_FLAG 16 -#define VP_CTRL_FLAG 32 -#define CAPTURE_FLAG 64 -#define DEBUG_FLAG 128 - -/* VPStatus */ -#define VP_STATE_OK 0x00 - -#define VP_STATE_FAILED_VIDEOINIT 0x01 -#define VP_STATE_FAILED_AECACBINIT 0x02 -#define VP_STATE_AEC_MAX 0x04 -#define VP_STATE_ACB_BMAX 0x08 - -#define VP_STATE_ACB_RMIN 0x10 -#define VP_STATE_ACB_GMIN 0x20 -#define VP_STATE_ACB_RMAX 0x40 -#define VP_STATE_ACB_GMAX 0x80 - -/* default (minimum) compensation values */ -#define COMP_RED 220 -#define COMP_GREEN1 214 -#define COMP_GREEN2 COMP_GREEN1 -#define COMP_BLUE 230 - -/* exposure status */ -#define EXPOSURE_VERY_LIGHT 0 -#define EXPOSURE_LIGHT 1 -#define EXPOSURE_NORMAL 2 -#define EXPOSURE_DARK 3 -#define EXPOSURE_VERY_DARK 4 - -/* ErrorCode */ -#define ERROR_FLICKER_BELOW_MIN_EXP 0x01 /*flicker exposure got below minimum exposure */ -#define ALOG(fmt,args...) printk(fmt, ##args) -#define LOG(fmt,args...) ALOG(KERN_INFO __FILE__ ":%s(%d):" fmt, __func__ , __LINE__ , ##args) - -#ifdef _CPIA_DEBUG_ -#define ADBG(fmt,args...) printk(fmt, jiffies, ##args) -#define DBG(fmt,args...) ADBG(KERN_DEBUG __FILE__" (%ld):%s(%d):" fmt, __func__, __LINE__ , ##args) -#else -#define DBG(fmn,args...) do {} while(0) -#endif - -#define DEB_BYTE(p)\ - DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\ - (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\ - (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0); - -#endif /* __KERNEL__ */ - -#endif /* cpia_h */ diff --git a/drivers/media/video/cpia_pp.c b/drivers/media/video/cpia_pp.c deleted file mode 100644 index f5604c16a092..000000000000 --- a/drivers/media/video/cpia_pp.c +++ /dev/null @@ -1,869 +0,0 @@ -/* - * cpia_pp CPiA Parallel Port driver - * - * Supports CPiA based parallel port Video Camera's. - * - * (C) Copyright 1999 Bas Huisman - * (C) Copyright 1999-2000 Scott J. Bertin , - * (C) Copyright 1999-2000 Peter Pregler - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ -/* #define _CPIA_DEBUG_ 1 */ - - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -/* #define _CPIA_DEBUG_ define for verbose debug output */ -#include "cpia.h" - -static int cpia_pp_open(void *privdata); -static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata), - void *cbdata); -static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data); -static int cpia_pp_streamStart(void *privdata); -static int cpia_pp_streamStop(void *privdata); -static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock); -static int cpia_pp_close(void *privdata); - - -#define ABOUT "Parallel port driver for Vision CPiA based cameras" - -#define PACKET_LENGTH 8 - -/* Magic numbers for defining port-device mappings */ -#define PPCPIA_PARPORT_UNSPEC -4 -#define PPCPIA_PARPORT_AUTO -3 -#define PPCPIA_PARPORT_OFF -2 -#define PPCPIA_PARPORT_NONE -1 - -static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; -static char *parport[PARPORT_MAX] = {NULL,}; - -MODULE_AUTHOR("B. Huisman & Peter Pregler "); -MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras"); -MODULE_LICENSE("GPL"); - -module_param_array(parport, charp, NULL, 0); -MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp."); - -struct pp_cam_entry { - struct pardevice *pdev; - struct parport *port; - struct work_struct cb_task; - void (*cb_func)(void *cbdata); - void *cb_data; - int open_count; - wait_queue_head_t wq_stream; - /* image state flags */ - int image_ready; /* we got an interrupt */ - int image_complete; /* we have seen 4 EOI */ - - int streaming; /* we are in streaming mode */ - int stream_irq; -}; - -static struct cpia_camera_ops cpia_pp_ops = -{ - cpia_pp_open, - cpia_pp_registerCallback, - cpia_pp_transferCmd, - cpia_pp_streamStart, - cpia_pp_streamStop, - cpia_pp_streamRead, - cpia_pp_close, - 1, - THIS_MODULE -}; - -static LIST_HEAD(cam_list); -static spinlock_t cam_list_lock_pp; - -/* FIXME */ -static void cpia_parport_enable_irq( struct parport *port ) { - parport_enable_irq(port); - mdelay(10); - return; -} - -static void cpia_parport_disable_irq( struct parport *port ) { - parport_disable_irq(port); - mdelay(10); - return; -} - -/* Special CPiA PPC modes: These are invoked by using the 1284 Extensibility - * Link Flag during negotiation */ -#define UPLOAD_FLAG 0x08 -#define NIBBLE_TRANSFER 0x01 -#define ECP_TRANSFER 0x03 - -#define PARPORT_CHUNK_SIZE PAGE_SIZE - - -static void cpia_pp_run_callback(struct work_struct *work) -{ - void (*cb_func)(void *cbdata); - void *cb_data; - struct pp_cam_entry *cam; - - cam = container_of(work, struct pp_cam_entry, cb_task); - cb_func = cam->cb_func; - cb_data = cam->cb_data; - - cb_func(cb_data); -} - -/**************************************************************************** - * - * CPiA-specific low-level parport functions for nibble uploads - * - ***************************************************************************/ -/* CPiA nonstandard "Nibble" mode (no nDataAvail signal after each byte). */ -/* The standard kernel parport_ieee1284_read_nibble() fails with the CPiA... */ - -static size_t cpia_read_nibble (struct parport *port, - void *buffer, size_t len, - int flags) -{ - /* adapted verbatim, with one change, from - parport_ieee1284_read_nibble() in drivers/parport/ieee1284-ops.c */ - - unsigned char *buf = buffer; - int i; - unsigned char byte = 0; - - len *= 2; /* in nibbles */ - for (i=0; i < len; i++) { - unsigned char nibble; - - /* The CPiA firmware suppresses the use of nDataAvail (nFault LO) - * after every second nibble to signal that more - * data is available. (the total number of Bytes that - * should be sent is known; if too few are received, an error - * will be recorded after a timeout). - * This is incompatible with parport_ieee1284_read_nibble(), - * which expects to find nFault LO after every second nibble. - */ - - /* Solution: modify cpia_read_nibble to only check for - * nDataAvail before the first nibble is sent. - */ - - /* Does the error line indicate end of data? */ - if (((i /*& 1*/) == 0) && - (parport_read_status(port) & PARPORT_STATUS_ERROR)) { - DBG("%s: No more nibble data (%d bytes)\n", - port->name, i/2); - goto end_of_data; - } - - /* Event 7: Set nAutoFd low. */ - parport_frob_control (port, - PARPORT_CONTROL_AUTOFD, - PARPORT_CONTROL_AUTOFD); - - /* Event 9: nAck goes low. */ - port->ieee1284.phase = IEEE1284_PH_REV_DATA; - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, 0)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 9 (%d bytes)\n", - port->name, i/2); - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - break; - } - - - /* Read a nibble. */ - nibble = parport_read_status (port) >> 3; - nibble &= ~8; - if ((nibble & 0x10) == 0) - nibble |= 8; - nibble &= 0xf; - - /* Event 10: Set nAutoFd high. */ - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - - /* Event 11: nAck goes high. */ - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, - PARPORT_STATUS_ACK)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 11\n", - port->name); - break; - } - - if (i & 1) { - /* Second nibble */ - byte |= nibble << 4; - *buf++ = byte; - } else - byte = nibble; - } - - if (i == len) { - /* Read the last nibble without checking data avail. */ - if (parport_read_status (port) & PARPORT_STATUS_ERROR) { - end_of_data: - /* Go to reverse idle phase. */ - parport_frob_control (port, - PARPORT_CONTROL_AUTOFD, - PARPORT_CONTROL_AUTOFD); - port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE; - } - else - port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL; - } - - return i/2; -} - -/* CPiA nonstandard "Nibble Stream" mode (2 nibbles per cycle, instead of 1) - * (See CPiA Data sheet p. 31) - * - * "Nibble Stream" mode used by CPiA for uploads to non-ECP ports is a - * nonstandard variant of nibble mode which allows the same (mediocre) - * data flow of 8 bits per cycle as software-enabled ECP by TRISTATE-capable - * parallel ports, but works also for non-TRISTATE-capable ports. - * (Standard nibble mode only send 4 bits per cycle) - * - */ - -static size_t cpia_read_nibble_stream(struct parport *port, - void *buffer, size_t len, - int flags) -{ - int i; - unsigned char *buf = buffer; - int endseen = 0; - - for (i=0; i < len; i++) { - unsigned char nibble[2], byte = 0; - int j; - - /* Image Data is complete when 4 consecutive EOI bytes (0xff) are seen */ - if (endseen > 3 ) - break; - - /* Event 7: Set nAutoFd low. */ - parport_frob_control (port, - PARPORT_CONTROL_AUTOFD, - PARPORT_CONTROL_AUTOFD); - - /* Event 9: nAck goes low. */ - port->ieee1284.phase = IEEE1284_PH_REV_DATA; - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, 0)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 9 (%d bytes)\n", - port->name, i/2); - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - break; - } - - /* Read lower nibble */ - nibble[0] = parport_read_status (port) >>3; - - /* Event 10: Set nAutoFd high. */ - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - - /* Event 11: nAck goes high. */ - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, - PARPORT_STATUS_ACK)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 11\n", - port->name); - break; - } - - /* Read upper nibble */ - nibble[1] = parport_read_status (port) >>3; - - /* reassemble the byte */ - for (j = 0; j < 2 ; j++ ) { - nibble[j] &= ~8; - if ((nibble[j] & 0x10) == 0) - nibble[j] |= 8; - nibble[j] &= 0xf; - } - byte = (nibble[0] |(nibble[1] << 4)); - *buf++ = byte; - - if(byte == EOI) - endseen++; - else - endseen = 0; - } - return i; -} - -/**************************************************************************** - * - * EndTransferMode - * - ***************************************************************************/ -static void EndTransferMode(struct pp_cam_entry *cam) -{ - parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); -} - -/**************************************************************************** - * - * ForwardSetup - * - ***************************************************************************/ -static int ForwardSetup(struct pp_cam_entry *cam) -{ - int retry; - - /* The CPiA uses ECP protocol for Downloads from the Host to the camera. - * This will be software-emulated if ECP hardware is not present - */ - - /* the usual camera maximum response time is 10ms, but after receiving - * some commands, it needs up to 40ms. (Data Sheet p. 32)*/ - - for(retry = 0; retry < 4; ++retry) { - if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) { - break; - } - mdelay(10); - } - if(retry == 4) { - DBG("Unable to negotiate IEEE1284 ECP Download mode\n"); - return -1; - } - return 0; -} -/**************************************************************************** - * - * ReverseSetup - * - ***************************************************************************/ -static int ReverseSetup(struct pp_cam_entry *cam, int extensibility) -{ - int retry; - int upload_mode, mode = IEEE1284_MODE_ECP; - int transfer_mode = ECP_TRANSFER; - - if (!(cam->port->modes & PARPORT_MODE_ECP) && - !(cam->port->modes & PARPORT_MODE_TRISTATE)) { - mode = IEEE1284_MODE_NIBBLE; - transfer_mode = NIBBLE_TRANSFER; - } - - upload_mode = mode; - if(extensibility) mode = UPLOAD_FLAG|transfer_mode|IEEE1284_EXT_LINK; - - /* the usual camera maximum response time is 10ms, but after - * receiving some commands, it needs up to 40ms. */ - - for(retry = 0; retry < 4; ++retry) { - if(!parport_negotiate(cam->port, mode)) { - break; - } - mdelay(10); - } - if(retry == 4) { - if(extensibility) - DBG("Unable to negotiate upload extensibility mode\n"); - else - DBG("Unable to negotiate upload mode\n"); - return -1; - } - if(extensibility) cam->port->ieee1284.mode = upload_mode; - return 0; -} - -/**************************************************************************** - * - * WritePacket - * - ***************************************************************************/ -static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size) -{ - int retval=0; - int size_written; - - if (packet == NULL) { - return -EINVAL; - } - if (ForwardSetup(cam)) { - DBG("Write failed in setup\n"); - return -EIO; - } - size_written = parport_write(cam->port, packet, size); - if(size_written != size) { - DBG("Write failed, wrote %d/%d\n", size_written, size); - retval = -EIO; - } - EndTransferMode(cam); - return retval; -} - -/**************************************************************************** - * - * ReadPacket - * - ***************************************************************************/ -static int ReadPacket(struct pp_cam_entry *cam, u8 *packet, size_t size) -{ - int retval=0; - - if (packet == NULL) { - return -EINVAL; - } - if (ReverseSetup(cam, 0)) { - return -EIO; - } - - /* support for CPiA variant nibble reads */ - if(cam->port->ieee1284.mode == IEEE1284_MODE_NIBBLE) { - if(cpia_read_nibble(cam->port, packet, size, 0) != size) - retval = -EIO; - } else { - if(parport_read(cam->port, packet, size) != size) - retval = -EIO; - } - EndTransferMode(cam); - return retval; -} - -/**************************************************************************** - * - * cpia_pp_streamStart - * - ***************************************************************************/ -static int cpia_pp_streamStart(void *privdata) -{ - struct pp_cam_entry *cam = privdata; - DBG("\n"); - cam->streaming=1; - cam->image_ready=0; - //if (ReverseSetup(cam,1)) return -EIO; - if(cam->stream_irq) cpia_parport_enable_irq(cam->port); - return 0; -} - -/**************************************************************************** - * - * cpia_pp_streamStop - * - ***************************************************************************/ -static int cpia_pp_streamStop(void *privdata) -{ - struct pp_cam_entry *cam = privdata; - - DBG("\n"); - cam->streaming=0; - cpia_parport_disable_irq(cam->port); - //EndTransferMode(cam); - - return 0; -} - -/**************************************************************************** - * - * cpia_pp_streamRead - * - ***************************************************************************/ -static int cpia_pp_read(struct parport *port, u8 *buffer, int len) -{ - int bytes_read; - - /* support for CPiA variant "nibble stream" reads */ - if(port->ieee1284.mode == IEEE1284_MODE_NIBBLE) - bytes_read = cpia_read_nibble_stream(port,buffer,len,0); - else { - int new_bytes; - for(bytes_read=0; bytes_readstreaming) DBG("%d / %d\n", cam->image_ready, noblock); - if( cam->stream_irq ) { - DBG("%d\n", cam->image_ready); - cam->image_ready--; - } - cam->image_complete=0; - if (0/*cam->streaming*/) { - if(!cam->image_ready) { - if(noblock) return -EWOULDBLOCK; - interruptible_sleep_on(&cam->wq_stream); - if( signal_pending(current) ) return -EINTR; - DBG("%d\n", cam->image_ready); - } - } else { - if (ReverseSetup(cam, 1)) { - DBG("unable to ReverseSetup\n"); - return -EIO; - } - } - endseen = 0; - block_size = PARPORT_CHUNK_SIZE; - while( !cam->image_complete ) { - cond_resched(); - - new_bytes = cpia_pp_read(cam->port, buffer, block_size ); - if( new_bytes <= 0 ) { - break; - } - i=-1; - while(++iimage_complete=1; - break; - } - if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) { - block_size=CPIA_MAX_IMAGE_SIZE-read_bytes; - } - } - EndTransferMode(cam); - return cam->image_complete ? read_bytes : -EIO; -} -/**************************************************************************** - * - * cpia_pp_transferCmd - * - ***************************************************************************/ -static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data) -{ - int err; - int retval=0; - int databytes; - struct pp_cam_entry *cam = privdata; - - if(cam == NULL) { - DBG("Internal driver error: cam is NULL\n"); - return -EINVAL; - } - if(command == NULL) { - DBG("Internal driver error: command is NULL\n"); - return -EINVAL; - } - databytes = (((int)command[7])<<8) | command[6]; - if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) { - DBG("Error writing command\n"); - return err; - } - if(command[0] == DATA_IN) { - u8 buffer[8]; - if(data == NULL) { - DBG("Internal driver error: data is NULL\n"); - return -EINVAL; - } - if((err = ReadPacket(cam, buffer, 8)) < 0) { - DBG("Error reading command result\n"); - return err; - } - memcpy(data, buffer, databytes); - } else if(command[0] == DATA_OUT) { - if(databytes > 0) { - if(data == NULL) { - DBG("Internal driver error: data is NULL\n"); - retval = -EINVAL; - } else { - if((err=WritePacket(cam, data, databytes)) < 0){ - DBG("Error writing command data\n"); - return err; - } - } - } - } else { - DBG("Unexpected first byte of command: %x\n", command[0]); - retval = -EINVAL; - } - return retval; -} - -/**************************************************************************** - * - * cpia_pp_open - * - ***************************************************************************/ -static int cpia_pp_open(void *privdata) -{ - struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata; - - if (cam == NULL) - return -EINVAL; - - if(cam->open_count == 0) { - if (parport_claim(cam->pdev)) { - DBG("failed to claim the port\n"); - return -EBUSY; - } - parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); - parport_data_forward(cam->port); - parport_write_control(cam->port, PARPORT_CONTROL_SELECT); - udelay(50); - parport_write_control(cam->port, - PARPORT_CONTROL_SELECT - | PARPORT_CONTROL_INIT); - } - - ++cam->open_count; - - return 0; -} - -/**************************************************************************** - * - * cpia_pp_registerCallback - * - ***************************************************************************/ -static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), void *cbdata) -{ - struct pp_cam_entry *cam = privdata; - int retval = 0; - - if(cam->port->irq != PARPORT_IRQ_NONE) { - cam->cb_func = cb; - cam->cb_data = cbdata; - INIT_WORK(&cam->cb_task, cpia_pp_run_callback); - } else { - retval = -1; - } - return retval; -} - -/**************************************************************************** - * - * cpia_pp_close - * - ***************************************************************************/ -static int cpia_pp_close(void *privdata) -{ - struct pp_cam_entry *cam = privdata; - if (--cam->open_count == 0) { - parport_release(cam->pdev); - } - return 0; -} - -/**************************************************************************** - * - * cpia_pp_register - * - ***************************************************************************/ -static int cpia_pp_register(struct parport *port) -{ - struct pardevice *pdev = NULL; - struct pp_cam_entry *cam; - struct cam_data *cpia; - - if (!(port->modes & PARPORT_MODE_PCSPP)) { - LOG("port is not supported by CPiA driver\n"); - return -ENXIO; - } - - cam = kzalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); - if (cam == NULL) { - LOG("failed to allocate camera structure\n"); - return -ENOMEM; - } - - pdev = parport_register_device(port, "cpia_pp", NULL, NULL, - NULL, 0, cam); - - if (!pdev) { - LOG("failed to parport_register_device\n"); - kfree(cam); - return -ENXIO; - } - - cam->pdev = pdev; - cam->port = port; - init_waitqueue_head(&cam->wq_stream); - - cam->streaming = 0; - cam->stream_irq = 0; - - if((cpia = cpia_register_camera(&cpia_pp_ops, cam)) == NULL) { - LOG("failed to cpia_register_camera\n"); - parport_unregister_device(pdev); - kfree(cam); - return -ENXIO; - } - spin_lock( &cam_list_lock_pp ); - list_add( &cpia->cam_data_list, &cam_list ); - spin_unlock( &cam_list_lock_pp ); - - return 0; -} - -static void cpia_pp_detach (struct parport *port) -{ - struct list_head *tmp; - struct cam_data *cpia = NULL; - struct pp_cam_entry *cam; - - spin_lock( &cam_list_lock_pp ); - list_for_each (tmp, &cam_list) { - cpia = list_entry(tmp, struct cam_data, cam_data_list); - cam = (struct pp_cam_entry *) cpia->lowlevel_data; - if (cam && cam->port->number == port->number) { - list_del(&cpia->cam_data_list); - break; - } - cpia = NULL; - } - spin_unlock( &cam_list_lock_pp ); - - if (!cpia) { - DBG("cpia_pp_detach failed to find cam_data in cam_list\n"); - return; - } - - cam = (struct pp_cam_entry *) cpia->lowlevel_data; - cpia_unregister_camera(cpia); - if(cam->open_count > 0) - cpia_pp_close(cam); - parport_unregister_device(cam->pdev); - cpia->lowlevel_data = NULL; - kfree(cam); -} - -static void cpia_pp_attach (struct parport *port) -{ - unsigned int i; - - switch (parport_nr[0]) - { - case PPCPIA_PARPORT_UNSPEC: - case PPCPIA_PARPORT_AUTO: - if (port->probe_info[0].class != PARPORT_CLASS_MEDIA || - port->probe_info[0].cmdset == NULL || - strncmp(port->probe_info[0].cmdset, "CPIA_1", 6) != 0) - return; - - cpia_pp_register(port); - - break; - - default: - for (i = 0; i < PARPORT_MAX; ++i) { - if (port->number == parport_nr[i]) { - cpia_pp_register(port); - break; - } - } - break; - } -} - -static struct parport_driver cpia_pp_driver = { - .name = "cpia_pp", - .attach = cpia_pp_attach, - .detach = cpia_pp_detach, -}; - -static int __init cpia_pp_init(void) -{ - printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, - CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER); - - if(parport_nr[0] == PPCPIA_PARPORT_OFF) { - printk(" disabled\n"); - return 0; - } - - spin_lock_init( &cam_list_lock_pp ); - - if (parport_register_driver (&cpia_pp_driver)) { - LOG ("unable to register with parport\n"); - return -EIO; - } - return 0; -} - -static int __init cpia_init(void) -{ - if (parport[0]) { - /* The user gave some parameters. Let's see what they were. */ - if (!strncmp(parport[0], "auto", 4)) { - parport_nr[0] = PPCPIA_PARPORT_AUTO; - } else { - int n; - for (n = 0; n < PARPORT_MAX && parport[n]; n++) { - if (!strncmp(parport[n], "none", 4)) { - parport_nr[n] = PPCPIA_PARPORT_NONE; - } else { - char *ep; - unsigned long r = simple_strtoul(parport[n], &ep, 0); - if (ep != parport[n]) { - parport_nr[n] = r; - } else { - LOG("bad port specifier `%s'\n", parport[n]); - return -ENODEV; - } - } - } - } - } - return cpia_pp_init(); -} - -static void __exit cpia_cleanup(void) -{ - parport_unregister_driver(&cpia_pp_driver); - return; -} - -module_init(cpia_init); -module_exit(cpia_cleanup); diff --git a/drivers/media/video/cpia_usb.c b/drivers/media/video/cpia_usb.c deleted file mode 100644 index 58d193ff591c..000000000000 --- a/drivers/media/video/cpia_usb.c +++ /dev/null @@ -1,640 +0,0 @@ -/* - * cpia_usb CPiA USB driver - * - * Supports CPiA based parallel port Video Camera's. - * - * Copyright (C) 1999 Jochen Scharrlach - * Copyright (C) 1999, 2000 Johannes Erdfelt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ -/* #define _CPIA_DEBUG_ 1 */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cpia.h" - -#define USB_REQ_CPIA_GRAB_FRAME 0xC1 -#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2 -#define WAIT_FOR_NEXT_FRAME 0 -#define FORCE_FRAME_UPLOAD 1 - -#define FRAMES_PER_DESC 10 -#define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */ -#define CPIA_NUMSBUF 2 -#define STREAM_BUF_SIZE (PAGE_SIZE * 4) -#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) - -struct cpia_sbuf { - char *data; - struct urb *urb; -}; - -#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100) -enum framebuf_status { - FRAME_EMPTY, - FRAME_READING, - FRAME_READY, - FRAME_ERROR, -}; - -struct framebuf { - int length; - enum framebuf_status status; - u8 data[FRAMEBUF_LEN]; - struct framebuf *next; -}; - -struct usb_cpia { - /* Device structure */ - struct usb_device *dev; - - unsigned char iface; - wait_queue_head_t wq_stream; - - int cursbuf; /* Current receiving sbuf */ - struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */ - - int streaming; - int open; - int present; - struct framebuf *buffers[3]; - struct framebuf *curbuff, *workbuff; -}; - -static int cpia_usb_open(void *privdata); -static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), - void *cbdata); -static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data); -static int cpia_usb_streamStart(void *privdata); -static int cpia_usb_streamStop(void *privdata); -static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock); -static int cpia_usb_close(void *privdata); - -#define ABOUT "USB driver for Vision CPiA based cameras" - -static struct cpia_camera_ops cpia_usb_ops = { - cpia_usb_open, - cpia_usb_registerCallback, - cpia_usb_transferCmd, - cpia_usb_streamStart, - cpia_usb_streamStop, - cpia_usb_streamRead, - cpia_usb_close, - 0, - THIS_MODULE -}; - -static LIST_HEAD(cam_list); -static spinlock_t cam_list_lock_usb; - -static void cpia_usb_complete(struct urb *urb) -{ - int i; - char *cdata; - struct usb_cpia *ucpia; - - if (!urb || !urb->context) - return; - - ucpia = (struct usb_cpia *) urb->context; - - if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open) - return; - - if (ucpia->workbuff->status == FRAME_EMPTY) { - ucpia->workbuff->status = FRAME_READING; - ucpia->workbuff->length = 0; - } - - for (i = 0; i < urb->number_of_packets; i++) { - int n = urb->iso_frame_desc[i].actual_length; - int st = urb->iso_frame_desc[i].status; - - cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - if (st) - printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st); - - if (FRAMEBUF_LEN < ucpia->workbuff->length + n) { - printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n); - return; - } - - if (n) { - if ((ucpia->workbuff->length > 0) || - (0x19 == cdata[0] && 0x68 == cdata[1])) { - memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n); - ucpia->workbuff->length += n; - } else - DBG("Ignoring packet!\n"); - } else { - if (ucpia->workbuff->length > 4 && - 0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] && - 0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] && - 0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] && - 0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) { - ucpia->workbuff->status = FRAME_READY; - ucpia->curbuff = ucpia->workbuff; - ucpia->workbuff = ucpia->workbuff->next; - ucpia->workbuff->status = FRAME_EMPTY; - ucpia->workbuff->length = 0; - - if (waitqueue_active(&ucpia->wq_stream)) - wake_up_interruptible(&ucpia->wq_stream); - } - } - } - - /* resubmit */ - urb->dev = ucpia->dev; - if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) - printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __func__, i); -} - -static int cpia_usb_open(void *privdata) -{ - struct usb_cpia *ucpia = (struct usb_cpia *) privdata; - struct urb *urb; - int ret, retval = 0, fx, err; - - if (!ucpia) - return -EINVAL; - - ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!ucpia->sbuf[0].data) - return -EINVAL; - - ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!ucpia->sbuf[1].data) { - retval = -EINVAL; - goto error_0; - } - - ret = usb_set_interface(ucpia->dev, ucpia->iface, 3); - if (ret < 0) { - printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret); - retval = -EBUSY; - goto error_1; - } - - ucpia->buffers[0]->status = FRAME_EMPTY; - ucpia->buffers[0]->length = 0; - ucpia->buffers[1]->status = FRAME_EMPTY; - ucpia->buffers[1]->length = 0; - ucpia->buffers[2]->status = FRAME_EMPTY; - ucpia->buffers[2]->length = 0; - ucpia->curbuff = ucpia->buffers[0]; - ucpia->workbuff = ucpia->buffers[1]; - - /* We double buffer the Iso lists, and also know the polling - * interval is every frame (1 == (1 << (bInterval -1))). - */ - urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); - if (!urb) { - printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); - retval = -ENOMEM; - goto error_1; - } - - ucpia->sbuf[0].urb = urb; - urb->dev = ucpia->dev; - urb->context = ucpia; - urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = ucpia->sbuf[0].data; - urb->complete = cpia_usb_complete; - urb->number_of_packets = FRAMES_PER_DESC; - urb->interval = 1; - urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; - for (fx = 0; fx < FRAMES_PER_DESC; fx++) { - urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; - urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; - } - - urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); - if (!urb) { - printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 1\n"); - retval = -ENOMEM; - goto error_urb0; - } - - ucpia->sbuf[1].urb = urb; - urb->dev = ucpia->dev; - urb->context = ucpia; - urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = ucpia->sbuf[1].data; - urb->complete = cpia_usb_complete; - urb->number_of_packets = FRAMES_PER_DESC; - urb->interval = 1; - urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; - for (fx = 0; fx < FRAMES_PER_DESC; fx++) { - urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; - urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; - } - - /* queue the ISO urbs, and resubmit in the completion handler */ - err = usb_submit_urb(ucpia->sbuf[0].urb, GFP_KERNEL); - if (err) { - printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n", - err); - goto error_urb1; - } - err = usb_submit_urb(ucpia->sbuf[1].urb, GFP_KERNEL); - if (err) { - printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n", - err); - goto error_urb1; - } - - ucpia->streaming = 1; - ucpia->open = 1; - - return 0; - -error_urb1: /* free urb 1 */ - usb_free_urb(ucpia->sbuf[1].urb); - ucpia->sbuf[1].urb = NULL; -error_urb0: /* free urb 0 */ - usb_free_urb(ucpia->sbuf[0].urb); - ucpia->sbuf[0].urb = NULL; -error_1: - kfree (ucpia->sbuf[1].data); - ucpia->sbuf[1].data = NULL; -error_0: - kfree (ucpia->sbuf[0].data); - ucpia->sbuf[0].data = NULL; - - return retval; -} - -// -// convenience functions -// - -/**************************************************************************** - * - * WritePacket - * - ***************************************************************************/ -static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size) -{ - if (!packet) - return -EINVAL; - - return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - packet[1] + (packet[0] << 8), - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - packet[2] + (packet[3] << 8), - packet[4] + (packet[5] << 8), buf, size, 1000); -} - -/**************************************************************************** - * - * ReadPacket - * - ***************************************************************************/ -static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size) -{ - if (!packet || size <= 0) - return -EINVAL; - - return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - packet[1] + (packet[0] << 8), - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - packet[2] + (packet[3] << 8), - packet[4] + (packet[5] << 8), buf, size, 1000); -} - -static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data) -{ - int err = 0; - int databytes; - struct usb_cpia *ucpia = (struct usb_cpia *)privdata; - struct usb_device *udev = ucpia->dev; - - if (!udev) { - DBG("Internal driver error: udev is NULL\n"); - return -EINVAL; - } - - if (!command) { - DBG("Internal driver error: command is NULL\n"); - return -EINVAL; - } - - databytes = (((int)command[7])<<8) | command[6]; - - if (command[0] == DATA_IN) { - u8 buffer[8]; - - if (!data) { - DBG("Internal driver error: data is NULL\n"); - return -EINVAL; - } - - err = ReadPacket(udev, command, buffer, 8); - if (err < 0) - return err; - - memcpy(data, buffer, databytes); - } else if(command[0] == DATA_OUT) - WritePacket(udev, command, data, databytes); - else { - DBG("Unexpected first byte of command: %x\n", command[0]); - err = -EINVAL; - } - - return 0; -} - -static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), - void *cbdata) -{ - return -ENODEV; -} - -static int cpia_usb_streamStart(void *privdata) -{ - return -ENODEV; -} - -static int cpia_usb_streamStop(void *privdata) -{ - return -ENODEV; -} - -static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock) -{ - struct usb_cpia *ucpia = (struct usb_cpia *) privdata; - struct framebuf *mybuff; - - if (!ucpia || !ucpia->present) - return -1; - - if (ucpia->curbuff->status != FRAME_READY) - interruptible_sleep_on(&ucpia->wq_stream); - else - DBG("Frame already waiting!\n"); - - mybuff = ucpia->curbuff; - - if (!mybuff) - return -1; - - if (mybuff->status != FRAME_READY || mybuff->length < 4) { - DBG("Something went wrong!\n"); - return -1; - } - - memcpy(frame, mybuff->data, mybuff->length); - mybuff->status = FRAME_EMPTY; - -/* DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n", */ -/* mybuff->length, frame[0], frame[1], */ -/* frame[mybuff->length-4], frame[mybuff->length-3], */ -/* frame[mybuff->length-2], frame[mybuff->length-1]); */ - - return mybuff->length; -} - -static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try) -{ - if (!ucpia->streaming) - return; - - ucpia->streaming = 0; - - /* Set packet size to 0 */ - if (try) { - int ret; - - ret = usb_set_interface(ucpia->dev, ucpia->iface, 0); - if (ret < 0) { - printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret); - return; - } - } - - /* Unschedule all of the iso td's */ - if (ucpia->sbuf[1].urb) { - usb_kill_urb(ucpia->sbuf[1].urb); - usb_free_urb(ucpia->sbuf[1].urb); - ucpia->sbuf[1].urb = NULL; - } - - kfree(ucpia->sbuf[1].data); - ucpia->sbuf[1].data = NULL; - - if (ucpia->sbuf[0].urb) { - usb_kill_urb(ucpia->sbuf[0].urb); - usb_free_urb(ucpia->sbuf[0].urb); - ucpia->sbuf[0].urb = NULL; - } - - kfree(ucpia->sbuf[0].data); - ucpia->sbuf[0].data = NULL; -} - -static int cpia_usb_close(void *privdata) -{ - struct usb_cpia *ucpia = (struct usb_cpia *) privdata; - - if(!ucpia) - return -ENODEV; - - ucpia->open = 0; - - /* ucpia->present = 0 protects against trying to reset the - * alt setting if camera is physically disconnected while open */ - cpia_usb_free_resources(ucpia, ucpia->present); - - return 0; -} - -/* Probing and initializing */ - -static int cpia_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct usb_host_interface *interface; - struct usb_cpia *ucpia; - struct cam_data *cam; - int ret; - - /* A multi-config CPiA camera? */ - if (udev->descriptor.bNumConfigurations != 1) - return -ENODEV; - - interface = intf->cur_altsetting; - - printk(KERN_INFO "USB CPiA camera found\n"); - - ucpia = kzalloc(sizeof(*ucpia), GFP_KERNEL); - if (!ucpia) { - printk(KERN_ERR "couldn't kmalloc cpia struct\n"); - return -ENOMEM; - } - - ucpia->dev = udev; - ucpia->iface = interface->desc.bInterfaceNumber; - init_waitqueue_head(&ucpia->wq_stream); - - ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0])); - if (!ucpia->buffers[0]) { - printk(KERN_ERR "couldn't vmalloc frame buffer 0\n"); - goto fail_alloc_0; - } - - ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1])); - if (!ucpia->buffers[1]) { - printk(KERN_ERR "couldn't vmalloc frame buffer 1\n"); - goto fail_alloc_1; - } - - ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2])); - if (!ucpia->buffers[2]) { - printk(KERN_ERR "couldn't vmalloc frame buffer 2\n"); - goto fail_alloc_2; - } - - ucpia->buffers[0]->next = ucpia->buffers[1]; - ucpia->buffers[1]->next = ucpia->buffers[2]; - ucpia->buffers[2]->next = ucpia->buffers[0]; - - ret = usb_set_interface(udev, ucpia->iface, 0); - if (ret < 0) { - printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret); - /* goto fail_all; */ - } - - /* Before register_camera, important */ - ucpia->present = 1; - - cam = cpia_register_camera(&cpia_usb_ops, ucpia); - if (!cam) { - LOG("failed to cpia_register_camera\n"); - goto fail_all; - } - - spin_lock( &cam_list_lock_usb ); - list_add( &cam->cam_data_list, &cam_list ); - spin_unlock( &cam_list_lock_usb ); - - usb_set_intfdata(intf, cam); - return 0; - -fail_all: - vfree(ucpia->buffers[2]); - ucpia->buffers[2] = NULL; -fail_alloc_2: - vfree(ucpia->buffers[1]); - ucpia->buffers[1] = NULL; -fail_alloc_1: - vfree(ucpia->buffers[0]); - ucpia->buffers[0] = NULL; -fail_alloc_0: - kfree(ucpia); - return -EIO; -} - -static void cpia_disconnect(struct usb_interface *intf); - -static struct usb_device_id cpia_id_table [] = { - { USB_DEVICE(0x0553, 0x0002) }, - { USB_DEVICE(0x0813, 0x0001) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, cpia_id_table); -MODULE_LICENSE("GPL"); - - -static struct usb_driver cpia_driver = { - .name = "cpia", - .probe = cpia_probe, - .disconnect = cpia_disconnect, - .id_table = cpia_id_table, -}; - -static void cpia_disconnect(struct usb_interface *intf) -{ - struct cam_data *cam = usb_get_intfdata(intf); - struct usb_cpia *ucpia; - - usb_set_intfdata(intf, NULL); - if (!cam) - return; - - ucpia = (struct usb_cpia *) cam->lowlevel_data; - spin_lock( &cam_list_lock_usb ); - list_del(&cam->cam_data_list); - spin_unlock( &cam_list_lock_usb ); - - ucpia->present = 0; - - cpia_unregister_camera(cam); - if(ucpia->open) - cpia_usb_close(cam->lowlevel_data); - - ucpia->curbuff->status = FRAME_ERROR; - - if (waitqueue_active(&ucpia->wq_stream)) - wake_up_interruptible(&ucpia->wq_stream); - - ucpia->curbuff = ucpia->workbuff = NULL; - - vfree(ucpia->buffers[2]); - ucpia->buffers[2] = NULL; - - vfree(ucpia->buffers[1]); - ucpia->buffers[1] = NULL; - - vfree(ucpia->buffers[0]); - ucpia->buffers[0] = NULL; - - cam->lowlevel_data = NULL; - kfree(ucpia); -} - -static int __init usb_cpia_init(void) -{ - printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, - CPIA_USB_MAJ_VER,CPIA_USB_MIN_VER,CPIA_USB_PATCH_VER); - - spin_lock_init(&cam_list_lock_usb); - return usb_register(&cpia_driver); -} - -static void __exit usb_cpia_cleanup(void) -{ - usb_deregister(&cpia_driver); -} - - -module_init (usb_cpia_init); -module_exit (usb_cpia_cleanup); - diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 335311a98fdc..303c52cfe915 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -51,6 +51,8 @@ source "drivers/staging/cx25821/Kconfig" source "drivers/staging/tm6000/Kconfig" +source "drivers/staging/cpia/Kconfig" + source "drivers/staging/usbip/Kconfig" source "drivers/staging/winbond/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index e3f1e1b6095e..ddcac245c99a 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_SLICOSS) += slicoss/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ +obj-$(CONFIG_VIDEO_CPIA) += cpia/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ obj-$(CONFIG_W35UND) += winbond/ diff --git a/drivers/staging/cpia/Kconfig b/drivers/staging/cpia/Kconfig new file mode 100644 index 000000000000..205d247ad373 --- /dev/null +++ b/drivers/staging/cpia/Kconfig @@ -0,0 +1,39 @@ +config VIDEO_CPIA + tristate "CPiA Video For Linux (DEPRECATED)" + depends on VIDEO_V4L1 + default n + ---help--- + This driver is DEPRECATED please use the gspca cpia1 module + instead. Note that you need atleast version 0.6.4 of libv4l for + the cpia1 gspca module. + + This is the video4linux driver for cameras based on Vision's CPiA + (Colour Processor Interface ASIC), such as the Creative Labs Video + Blaster Webcam II. If you have one of these cameras, say Y here + and select parallel port and/or USB lowlevel support below, + otherwise say N. This will not work with the Creative Webcam III. + + Please read for more + information. + + This driver is also available as a module (cpia). + +config VIDEO_CPIA_PP + tristate "CPiA Parallel Port Lowlevel Support" + depends on PARPORT_1284 && VIDEO_CPIA && PARPORT + help + This is the lowlevel parallel port support for cameras based on + Vision's CPiA (Colour Processor Interface ASIC), such as the + Creative Webcam II. If you have the parallel port version of one + of these cameras, say Y here, otherwise say N. It is also available + as a module (cpia_pp). + +config VIDEO_CPIA_USB + tristate "CPiA USB Lowlevel Support" + depends on VIDEO_CPIA && USB + help + This is the lowlevel USB support for cameras based on Vision's CPiA + (Colour Processor Interface ASIC), such as the Creative Webcam II. + If you have the USB version of one of these cameras, say Y here, + otherwise say N. This will not work with the Creative Webcam III. + It is also available as a module (cpia_usb). diff --git a/drivers/staging/cpia/Makefile b/drivers/staging/cpia/Makefile new file mode 100644 index 000000000000..89e52f10d739 --- /dev/null +++ b/drivers/staging/cpia/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_VIDEO_CPIA) += cpia.o +obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o +obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o + +EXTRA_CFLAGS += -Idrivers/media/video diff --git a/drivers/staging/cpia/TODO b/drivers/staging/cpia/TODO new file mode 100644 index 000000000000..ccb1c0775eec --- /dev/null +++ b/drivers/staging/cpia/TODO @@ -0,0 +1,8 @@ +This is an obsolete driver for some cpia-based webcams that use the parallel port. +We couldn't find anyone with this hardware in order to port it to use V4L2. + +Also, parallel-port webcams are obsolete nowadays. + +If nobody take care on it, the driver will be removed for 2.6.38. + +Please send patches to linux-media@vger.kernel.org diff --git a/drivers/staging/cpia/cpia.c b/drivers/staging/cpia/cpia.c new file mode 100644 index 000000000000..933ae4c8cb9a --- /dev/null +++ b/drivers/staging/cpia/cpia.c @@ -0,0 +1,4032 @@ +/* + * cpia CPiA driver + * + * Supports CPiA based Video Camera's. + * + * (C) Copyright 1999-2000 Peter Pregler + * (C) Copyright 1999-2000 Scott J. Bertin + * (C) Copyright 1999-2000 Johannes Erdfelt + * (C) Copyright 2000 STMicroelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ +/* #define _CPIA_DEBUG_ 1 */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpia.h" + +static int video_nr = -1; + +#ifdef MODULE +module_param(video_nr, int, 0); +MODULE_AUTHOR("Scott J. Bertin & Peter Pregler & Johannes Erdfelt "); +MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("video"); +#endif + +static unsigned short colorspace_conv; +module_param(colorspace_conv, ushort, 0444); +MODULE_PARM_DESC(colorspace_conv, + " Colorspace conversion:" + "\n 0 = disable, 1 = enable" + "\n Default value is 0" + ); + +#define ABOUT "V4L-Driver for Vision CPiA based cameras" + +#define CPIA_MODULE_CPIA (0<<5) +#define CPIA_MODULE_SYSTEM (1<<5) +#define CPIA_MODULE_VP_CTRL (5<<5) +#define CPIA_MODULE_CAPTURE (6<<5) +#define CPIA_MODULE_DEBUG (7<<5) + +#define INPUT (DATA_IN << 8) +#define OUTPUT (DATA_OUT << 8) + +#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1) +#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2) +#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3) +#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4) +#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5) +#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7) +#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8) +#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10) + +#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1) +#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2) +#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3) +#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4) +#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5) +#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6) +#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7) +#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8) +#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9) +#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10) +#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11) +#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12) +#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13) + +#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1) +#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2) +#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3) +#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4) +#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6) +#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7) +#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8) +#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9) +#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10) +#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11) +#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16) +#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17) +#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18) +#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19) +#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25) +#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30) +#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31) + +#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1) +#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2) +#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3) +#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4) +#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5) +#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6) +#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7) +#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8) +#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9) +#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10) +#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11) +#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12) +#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13) +#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14) +#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15) + +#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1) +#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4) +#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5) +#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6) +#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8) +#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9) +#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10) +#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11) + +enum { + FRAME_READY, /* Ready to grab into */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_UNUSED, /* Unused (no MCAPTURE) */ +}; + +#define COMMAND_NONE 0x0000 +#define COMMAND_SETCOMPRESSION 0x0001 +#define COMMAND_SETCOMPRESSIONTARGET 0x0002 +#define COMMAND_SETCOLOURPARAMS 0x0004 +#define COMMAND_SETFORMAT 0x0008 +#define COMMAND_PAUSE 0x0010 +#define COMMAND_RESUME 0x0020 +#define COMMAND_SETYUVTHRESH 0x0040 +#define COMMAND_SETECPTIMING 0x0080 +#define COMMAND_SETCOMPRESSIONPARAMS 0x0100 +#define COMMAND_SETEXPOSURE 0x0200 +#define COMMAND_SETCOLOURBALANCE 0x0400 +#define COMMAND_SETSENSORFPS 0x0800 +#define COMMAND_SETAPCOR 0x1000 +#define COMMAND_SETFLICKERCTRL 0x2000 +#define COMMAND_SETVLOFFSET 0x4000 +#define COMMAND_SETLIGHTS 0x8000 + +#define ROUND_UP_EXP_FOR_FLICKER 15 + +/* Constants for automatic frame rate adjustment */ +#define MAX_EXP 302 +#define MAX_EXP_102 255 +#define LOW_EXP 140 +#define VERY_LOW_EXP 70 +#define TC 94 +#define EXP_ACC_DARK 50 +#define EXP_ACC_LIGHT 90 +#define HIGH_COMP_102 160 +#define MAX_COMP 239 +#define DARK_TIME 3 +#define LIGHT_TIME 3 + +/* Maximum number of 10ms loops to wait for the stream to become ready */ +#define READY_TIMEOUT 100 + +/* Developer's Guide Table 5 p 3-34 + * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/ +static u8 flicker_jumps[2][2][4] = +{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } }, + { { 64, 32, 16, 8 }, { 76, 38, 19, 9} } +}; + +/* forward declaration of local function */ +static void reset_camera_struct(struct cam_data *cam); +static int find_over_exposure(int brightness); +static void set_flicker(struct cam_params *params, volatile u32 *command_flags, + int on); + + +/********************************************************************** + * + * Memory management + * + **********************************************************************/ +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + +/********************************************************************** + * + * /proc interface + * + **********************************************************************/ +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *cpia_proc_root=NULL; + +static int cpia_proc_show(struct seq_file *m, void *v) +{ + struct cam_data *cam = m->private; + int tmp; + char tmpstr[29]; + + seq_printf(m, "read-only\n-----------------------\n"); + seq_printf(m, "V4L Driver version: %d.%d.%d\n", + CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); + seq_printf(m, "CPIA Version: %d.%02d (%d.%d)\n", + cam->params.version.firmwareVersion, + cam->params.version.firmwareRevision, + cam->params.version.vcVersion, + cam->params.version.vcRevision); + seq_printf(m, "CPIA PnP-ID: %04x:%04x:%04x\n", + cam->params.pnpID.vendor, cam->params.pnpID.product, + cam->params.pnpID.deviceRevision); + seq_printf(m, "VP-Version: %d.%d %04x\n", + cam->params.vpVersion.vpVersion, + cam->params.vpVersion.vpRevision, + cam->params.vpVersion.cameraHeadID); + + seq_printf(m, "system_state: %#04x\n", + cam->params.status.systemState); + seq_printf(m, "grab_state: %#04x\n", + cam->params.status.grabState); + seq_printf(m, "stream_state: %#04x\n", + cam->params.status.streamState); + seq_printf(m, "fatal_error: %#04x\n", + cam->params.status.fatalError); + seq_printf(m, "cmd_error: %#04x\n", + cam->params.status.cmdError); + seq_printf(m, "debug_flags: %#04x\n", + cam->params.status.debugFlags); + seq_printf(m, "vp_status: %#04x\n", + cam->params.status.vpStatus); + seq_printf(m, "error_code: %#04x\n", + cam->params.status.errorCode); + /* QX3 specific entries */ + if (cam->params.qx3.qx3_detected) { + seq_printf(m, "button: %4d\n", + cam->params.qx3.button); + seq_printf(m, "cradled: %4d\n", + cam->params.qx3.cradled); + } + seq_printf(m, "video_size: %s\n", + cam->params.format.videoSize == VIDEOSIZE_CIF ? + "CIF " : "QCIF"); + seq_printf(m, "roi: (%3d, %3d) to (%3d, %3d)\n", + cam->params.roi.colStart*8, + cam->params.roi.rowStart*4, + cam->params.roi.colEnd*8, + cam->params.roi.rowEnd*4); + seq_printf(m, "actual_fps: %3d\n", cam->fps); + seq_printf(m, "transfer_rate: %4dkB/s\n", + cam->transfer_rate); + + seq_printf(m, "\nread-write\n"); + seq_printf(m, "----------------------- current min" + " max default comment\n"); + seq_printf(m, "brightness: %8d %8d %8d %8d\n", + cam->params.colourParams.brightness, 0, 100, 50); + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits contrast to 80 */ + tmp = 80; + else + tmp = 96; + + seq_printf(m, "contrast: %8d %8d %8d %8d" + " steps of 8\n", + cam->params.colourParams.contrast, 0, tmp, 48); + seq_printf(m, "saturation: %8d %8d %8d %8d\n", + cam->params.colourParams.saturation, 0, 100, 50); + tmp = (25000+5000*cam->params.sensorFps.baserate)/ + (1<params.sensorFps.divisor); + seq_printf(m, "sensor_fps: %4d.%03d %8d %8d %8d\n", + tmp/1000, tmp%1000, 3, 30, 15); + seq_printf(m, "stream_start_line: %8d %8d %8d %8d\n", + 2*cam->params.streamStartLine, 0, + cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144, + cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120); + seq_printf(m, "sub_sample: %8s %8s %8s %8s\n", + cam->params.format.subSample == SUBSAMPLE_420 ? + "420" : "422", "420", "422", "422"); + seq_printf(m, "yuv_order: %8s %8s %8s %8s\n", + cam->params.format.yuvOrder == YUVORDER_YUYV ? + "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV"); + seq_printf(m, "ecp_timing: %8s %8s %8s %8s\n", + cam->params.ecpTiming ? "slow" : "normal", "slow", + "normal", "normal"); + + if (cam->params.colourBalance.balanceMode == 2) { + sprintf(tmpstr, "auto"); + } else { + sprintf(tmpstr, "manual"); + } + seq_printf(m, "color_balance_mode: %8s %8s %8s" + " %8s\n", tmpstr, "manual", "auto", "auto"); + seq_printf(m, "red_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.redGain, 0, 212, 32); + seq_printf(m, "green_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.greenGain, 0, 212, 6); + seq_printf(m, "blue_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.blueGain, 0, 212, 92); + + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits gain to 2 */ + sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2); + else + sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2); + + if (cam->params.exposure.gainMode == 0) + seq_printf(m, "max_gain: unknown %28s" + " powers of 2\n", tmpstr); + else + seq_printf(m, "max_gain: %8d %28s" + " 1,2,4 or 8 \n", + 1<<(cam->params.exposure.gainMode-1), tmpstr); + + switch(cam->params.exposure.expMode) { + case 1: + case 3: + sprintf(tmpstr, "manual"); + break; + case 2: + sprintf(tmpstr, "auto"); + break; + default: + sprintf(tmpstr, "unknown"); + break; + } + seq_printf(m, "exposure_mode: %8s %8s %8s" + " %8s\n", tmpstr, "manual", "auto", "auto"); + seq_printf(m, "centre_weight: %8s %8s %8s %8s\n", + (2-cam->params.exposure.centreWeight) ? "on" : "off", + "off", "on", "on"); + seq_printf(m, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n", + 1<params.exposure.gain, 1, 1); + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits fineExp/2 to 127 */ + tmp = 254; + else + tmp = 510; + + seq_printf(m, "fine_exp: %8d %8d %8d %8d\n", + cam->params.exposure.fineExp*2, 0, tmp, 0); + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits coarseExpHi to 0 */ + tmp = MAX_EXP_102; + else + tmp = MAX_EXP; + + seq_printf(m, "coarse_exp: %8d %8d %8d" + " %8d\n", cam->params.exposure.coarseExpLo+ + 256*cam->params.exposure.coarseExpHi, 0, tmp, 185); + seq_printf(m, "red_comp: %8d %8d %8d %8d\n", + cam->params.exposure.redComp, COMP_RED, 255, COMP_RED); + seq_printf(m, "green1_comp: %8d %8d %8d %8d\n", + cam->params.exposure.green1Comp, COMP_GREEN1, 255, + COMP_GREEN1); + seq_printf(m, "green2_comp: %8d %8d %8d %8d\n", + cam->params.exposure.green2Comp, COMP_GREEN2, 255, + COMP_GREEN2); + seq_printf(m, "blue_comp: %8d %8d %8d %8d\n", + cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE); + + seq_printf(m, "apcor_gain1: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain1, 0, 0xff, 0x1c); + seq_printf(m, "apcor_gain2: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain2, 0, 0xff, 0x1a); + seq_printf(m, "apcor_gain4: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain4, 0, 0xff, 0x2d); + seq_printf(m, "apcor_gain8: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain8, 0, 0xff, 0x2a); + seq_printf(m, "vl_offset_gain1: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain1, 0, 255, 24); + seq_printf(m, "vl_offset_gain2: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain2, 0, 255, 28); + seq_printf(m, "vl_offset_gain4: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain4, 0, 255, 30); + seq_printf(m, "vl_offset_gain8: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain8, 0, 255, 30); + seq_printf(m, "flicker_control: %8s %8s %8s %8s\n", + cam->params.flickerControl.flickerMode ? "on" : "off", + "off", "on", "off"); + seq_printf(m, "mains_frequency: %8d %8d %8d %8d" + " only 50/60\n", + cam->mainsFreq ? 60 : 50, 50, 60, 50); + if(cam->params.flickerControl.allowableOverExposure < 0) + seq_printf(m, "allowable_overexposure: %4dauto auto %8d auto\n", + -cam->params.flickerControl.allowableOverExposure, + 255); + else + seq_printf(m, "allowable_overexposure: %8d auto %8d auto\n", + cam->params.flickerControl.allowableOverExposure, + 255); + seq_printf(m, "compression_mode: "); + switch(cam->params.compression.mode) { + case CPIA_COMPRESSION_NONE: + seq_printf(m, "%8s", "none"); + break; + case CPIA_COMPRESSION_AUTO: + seq_printf(m, "%8s", "auto"); + break; + case CPIA_COMPRESSION_MANUAL: + seq_printf(m, "%8s", "manual"); + break; + default: + seq_printf(m, "%8s", "unknown"); + break; + } + seq_printf(m, " none,auto,manual auto\n"); + seq_printf(m, "decimation_enable: %8s %8s %8s %8s\n", + cam->params.compression.decimation == + DECIMATION_ENAB ? "on":"off", "off", "on", + "off"); + seq_printf(m, "compression_target: %9s %9s %9s %9s\n", + cam->params.compressionTarget.frTargeting == + CPIA_COMPRESSION_TARGET_FRAMERATE ? + "framerate":"quality", + "framerate", "quality", "quality"); + seq_printf(m, "target_framerate: %8d %8d %8d %8d\n", + cam->params.compressionTarget.targetFR, 1, 30, 15); + seq_printf(m, "target_quality: %8d %8d %8d %8d\n", + cam->params.compressionTarget.targetQ, 1, 64, 5); + seq_printf(m, "y_threshold: %8d %8d %8d %8d\n", + cam->params.yuvThreshold.yThreshold, 0, 31, 6); + seq_printf(m, "uv_threshold: %8d %8d %8d %8d\n", + cam->params.yuvThreshold.uvThreshold, 0, 31, 6); + seq_printf(m, "hysteresis: %8d %8d %8d %8d\n", + cam->params.compressionParams.hysteresis, 0, 255, 3); + seq_printf(m, "threshold_max: %8d %8d %8d %8d\n", + cam->params.compressionParams.threshMax, 0, 255, 11); + seq_printf(m, "small_step: %8d %8d %8d %8d\n", + cam->params.compressionParams.smallStep, 0, 255, 1); + seq_printf(m, "large_step: %8d %8d %8d %8d\n", + cam->params.compressionParams.largeStep, 0, 255, 3); + seq_printf(m, "decimation_hysteresis: %8d %8d %8d %8d\n", + cam->params.compressionParams.decimationHysteresis, + 0, 255, 2); + seq_printf(m, "fr_diff_step_thresh: %8d %8d %8d %8d\n", + cam->params.compressionParams.frDiffStepThresh, + 0, 255, 5); + seq_printf(m, "q_diff_step_thresh: %8d %8d %8d %8d\n", + cam->params.compressionParams.qDiffStepThresh, + 0, 255, 3); + seq_printf(m, "decimation_thresh_mod: %8d %8d %8d %8d\n", + cam->params.compressionParams.decimationThreshMod, + 0, 255, 2); + /* QX3 specific entries */ + if (cam->params.qx3.qx3_detected) { + seq_printf(m, "toplight: %8s %8s %8s %8s\n", + cam->params.qx3.toplight ? "on" : "off", + "off", "on", "off"); + seq_printf(m, "bottomlight: %8s %8s %8s %8s\n", + cam->params.qx3.bottomlight ? "on" : "off", + "off", "on", "off"); + } + + return 0; +} + +static int cpia_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cpia_proc_show, PDE(inode)->data); +} + +static int match(char *checkstr, char **buffer, size_t *count, + int *find_colon, int *err) +{ + int ret, colon_found = 1; + int len = strlen(checkstr); + ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0); + if (ret) { + *buffer += len; + *count -= len; + if (*find_colon) { + colon_found = 0; + while (*count && (**buffer == ' ' || **buffer == '\t' || + (!colon_found && **buffer == ':'))) { + if (**buffer == ':') + colon_found = 1; + --*count; + ++*buffer; + } + if (!*count || !colon_found) + *err = -EINVAL; + *find_colon = 0; + } + } + return ret; +} + +static unsigned long int value(char **buffer, size_t *count, int *err) +{ + char *p; + unsigned long int ret; + ret = simple_strtoul(*buffer, &p, 0); + if (p == *buffer) + *err = -EINVAL; + else { + *count -= p - *buffer; + *buffer = p; + } + return ret; +} + +static ssize_t cpia_proc_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct cam_data *cam = PDE(file->f_path.dentry->d_inode)->data; + struct cam_params new_params; + char *page, *buffer; + int retval, find_colon; + int size = count; + unsigned long val = 0; + u32 command_flags = 0; + u8 new_mains; + + /* + * This code to copy from buf to page is shamelessly copied + * from the comx driver + */ + if (count > PAGE_SIZE) { + printk(KERN_ERR "count is %zu > %d!!!\n", count, (int)PAGE_SIZE); + return -ENOSPC; + } + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; + + if(copy_from_user(page, buf, count)) + { + retval = -EFAULT; + goto out; + } + + if (page[count-1] == '\n') + page[count-1] = '\0'; + else if (count < PAGE_SIZE) + page[count] = '\0'; + else if (page[count]) { + retval = -EINVAL; + goto out; + } + + buffer = page; + + if (mutex_lock_interruptible(&cam->param_lock)) + return -ERESTARTSYS; + + /* + * Skip over leading whitespace + */ + while (count && isspace(*buffer)) { + --count; + ++buffer; + } + + memcpy(&new_params, &cam->params, sizeof(struct cam_params)); + new_mains = cam->mainsFreq; + +#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval)) +#define VALUE (value(&buffer,&count, &retval)) +#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \ + new_params.version.firmwareRevision == (y)) + + retval = 0; + while (count && !retval) { + find_colon = 1; + if (MATCH("brightness")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) + new_params.colourParams.brightness = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + if(new_params.flickerControl.allowableOverExposure < 0) + new_params.flickerControl.allowableOverExposure = + -find_over_exposure(new_params.colourParams.brightness); + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + + } else if (MATCH("contrast")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) { + /* contrast is in steps of 8, so round*/ + val = ((val + 3) / 8) * 8; + /* 1-02 firmware limits contrast to 80*/ + if (FIRMWARE_VERSION(1,2) && val > 80) + val = 80; + + new_params.colourParams.contrast = val; + } else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + } else if (MATCH("saturation")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) + new_params.colourParams.saturation = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + } else if (MATCH("sensor_fps")) { + if (!retval) + val = VALUE; + + if (!retval) { + /* find values so that sensorFPS is minimized, + * but >= val */ + if (val > 30) + retval = -EINVAL; + else if (val > 25) { + new_params.sensorFps.divisor = 0; + new_params.sensorFps.baserate = 1; + } else if (val > 15) { + new_params.sensorFps.divisor = 0; + new_params.sensorFps.baserate = 0; + } else if (val > 12) { + new_params.sensorFps.divisor = 1; + new_params.sensorFps.baserate = 1; + } else if (val > 7) { + new_params.sensorFps.divisor = 1; + new_params.sensorFps.baserate = 0; + } else if (val > 6) { + new_params.sensorFps.divisor = 2; + new_params.sensorFps.baserate = 1; + } else if (val > 3) { + new_params.sensorFps.divisor = 2; + new_params.sensorFps.baserate = 0; + } else { + new_params.sensorFps.divisor = 3; + /* Either base rate would work here */ + new_params.sensorFps.baserate = 1; + } + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if (new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } + command_flags |= COMMAND_SETSENSORFPS; + cam->exposure_status = EXPOSURE_NORMAL; + } else if (MATCH("stream_start_line")) { + if (!retval) + val = VALUE; + + if (!retval) { + int max_line = 288; + + if (new_params.format.videoSize == VIDEOSIZE_QCIF) + max_line = 144; + if (val <= max_line) + new_params.streamStartLine = val/2; + else + retval = -EINVAL; + } + } else if (MATCH("sub_sample")) { + if (!retval && MATCH("420")) + new_params.format.subSample = SUBSAMPLE_420; + else if (!retval && MATCH("422")) + new_params.format.subSample = SUBSAMPLE_422; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETFORMAT; + } else if (MATCH("yuv_order")) { + if (!retval && MATCH("YUYV")) + new_params.format.yuvOrder = YUVORDER_YUYV; + else if (!retval && MATCH("UYVY")) + new_params.format.yuvOrder = YUVORDER_UYVY; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETFORMAT; + } else if (MATCH("ecp_timing")) { + if (!retval && MATCH("normal")) + new_params.ecpTiming = 0; + else if (!retval && MATCH("slow")) + new_params.ecpTiming = 1; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETECPTIMING; + } else if (MATCH("color_balance_mode")) { + if (!retval && MATCH("manual")) + new_params.colourBalance.balanceMode = 3; + else if (!retval && MATCH("auto")) + new_params.colourBalance.balanceMode = 2; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("red_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) { + new_params.colourBalance.redGain = val; + new_params.colourBalance.balanceMode = 1; + } else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("green_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) { + new_params.colourBalance.greenGain = val; + new_params.colourBalance.balanceMode = 1; + } else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("blue_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) { + new_params.colourBalance.blueGain = val; + new_params.colourBalance.balanceMode = 1; + } else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("max_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + /* 1-02 firmware limits gain to 2 */ + if (FIRMWARE_VERSION(1,2) && val > 2) + val = 2; + switch(val) { + case 1: + new_params.exposure.gainMode = 1; + break; + case 2: + new_params.exposure.gainMode = 2; + break; + case 4: + new_params.exposure.gainMode = 3; + break; + case 8: + new_params.exposure.gainMode = 4; + break; + default: + retval = -EINVAL; + break; + } + } + command_flags |= COMMAND_SETEXPOSURE; + } else if (MATCH("exposure_mode")) { + if (!retval && MATCH("auto")) + new_params.exposure.expMode = 2; + else if (!retval && MATCH("manual")) { + if (new_params.exposure.expMode == 2) + new_params.exposure.expMode = 3; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + new_params.flickerControl.flickerMode = 0; + } else + retval = -EINVAL; + + command_flags |= COMMAND_SETEXPOSURE; + } else if (MATCH("centre_weight")) { + if (!retval && MATCH("on")) + new_params.exposure.centreWeight = 1; + else if (!retval && MATCH("off")) + new_params.exposure.centreWeight = 2; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETEXPOSURE; + } else if (MATCH("gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + switch(val) { + case 1: + new_params.exposure.gain = 0; + break; + case 2: + new_params.exposure.gain = 1; + break; + case 4: + new_params.exposure.gain = 2; + break; + case 8: + new_params.exposure.gain = 3; + break; + default: + retval = -EINVAL; + break; + } + new_params.exposure.expMode = 1; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETEXPOSURE; + if (new_params.exposure.gain > + new_params.exposure.gainMode-1) + retval = -EINVAL; + } + } else if (MATCH("fine_exp")) { + if (!retval) + val = VALUE/2; + + if (!retval) { + if (val < 256) { + /* 1-02 firmware limits fineExp/2 to 127*/ + if (FIRMWARE_VERSION(1,2) && val > 127) + val = 127; + new_params.exposure.fineExp = val; + new_params.exposure.expMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } + } else if (MATCH("coarse_exp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= MAX_EXP) { + if (FIRMWARE_VERSION(1,2) && + val > MAX_EXP_102) + val = MAX_EXP_102; + new_params.exposure.coarseExpLo = + val & 0xff; + new_params.exposure.coarseExpHi = + val >> 8; + new_params.exposure.expMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } + } else if (MATCH("red_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_RED && val <= 255) { + new_params.exposure.redComp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("green1_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_GREEN1 && val <= 255) { + new_params.exposure.green1Comp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("green2_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_GREEN2 && val <= 255) { + new_params.exposure.green2Comp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("blue_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_BLUE && val <= 255) { + new_params.exposure.blueComp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain1")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain1 = val; + else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain2")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain2 = val; + else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain4")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain4 = val; + else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain8")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain8 = val; + else + retval = -EINVAL; + } + } else if (MATCH("vl_offset_gain1")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain1 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("vl_offset_gain2")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain2 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("vl_offset_gain4")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain4 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("vl_offset_gain8")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain8 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("flicker_control")) { + if (!retval && MATCH("on")) { + set_flicker(&new_params, &command_flags, 1); + } else if (!retval && MATCH("off")) { + set_flicker(&new_params, &command_flags, 0); + } else + retval = -EINVAL; + + command_flags |= COMMAND_SETFLICKERCTRL; + } else if (MATCH("mains_frequency")) { + if (!retval && MATCH("50")) { + new_mains = 0; + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if (new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } else if (!retval && MATCH("60")) { + new_mains = 1; + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if (new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } else if (MATCH("allowable_overexposure")) { + if (!retval && MATCH("auto")) { + new_params.flickerControl.allowableOverExposure = + -find_over_exposure(new_params.colourParams.brightness); + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + } else { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) { + new_params.flickerControl. + allowableOverExposure = val; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } + } + } else if (MATCH("compression_mode")) { + if (!retval && MATCH("none")) + new_params.compression.mode = + CPIA_COMPRESSION_NONE; + else if (!retval && MATCH("auto")) + new_params.compression.mode = + CPIA_COMPRESSION_AUTO; + else if (!retval && MATCH("manual")) + new_params.compression.mode = + CPIA_COMPRESSION_MANUAL; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOMPRESSION; + } else if (MATCH("decimation_enable")) { + if (!retval && MATCH("off")) + new_params.compression.decimation = 0; + else if (!retval && MATCH("on")) + new_params.compression.decimation = 1; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOMPRESSION; + } else if (MATCH("compression_target")) { + if (!retval && MATCH("quality")) + new_params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_QUALITY; + else if (!retval && MATCH("framerate")) + new_params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_FRAMERATE; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + } else if (MATCH("target_framerate")) { + if (!retval) + val = VALUE; + + if (!retval) { + if(val > 0 && val <= 30) + new_params.compressionTarget.targetFR = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + } else if (MATCH("target_quality")) { + if (!retval) + val = VALUE; + + if (!retval) { + if(val > 0 && val <= 64) + new_params.compressionTarget.targetQ = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + } else if (MATCH("y_threshold")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 32) + new_params.yuvThreshold.yThreshold = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETYUVTHRESH; + } else if (MATCH("uv_threshold")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 32) + new_params.yuvThreshold.uvThreshold = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETYUVTHRESH; + } else if (MATCH("hysteresis")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.hysteresis = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("threshold_max")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.threshMax = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("small_step")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.smallStep = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("large_step")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.largeStep = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("decimation_hysteresis")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.decimationHysteresis = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("fr_diff_step_thresh")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.frDiffStepThresh = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("q_diff_step_thresh")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.qDiffStepThresh = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("decimation_thresh_mod")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.decimationThreshMod = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("toplight")) { + if (!retval && MATCH("on")) + new_params.qx3.toplight = 1; + else if (!retval && MATCH("off")) + new_params.qx3.toplight = 0; + else + retval = -EINVAL; + command_flags |= COMMAND_SETLIGHTS; + } else if (MATCH("bottomlight")) { + if (!retval && MATCH("on")) + new_params.qx3.bottomlight = 1; + else if (!retval && MATCH("off")) + new_params.qx3.bottomlight = 0; + else + retval = -EINVAL; + command_flags |= COMMAND_SETLIGHTS; + } else { + DBG("No match found\n"); + retval = -EINVAL; + } + + if (!retval) { + while (count && isspace(*buffer) && *buffer != '\n') { + --count; + ++buffer; + } + if (count) { + if (*buffer == '\0' && count != 1) + retval = -EINVAL; + else if (*buffer != '\n' && *buffer != ';' && + *buffer != '\0') + retval = -EINVAL; + else { + --count; + ++buffer; + } + } + } + } +#undef MATCH +#undef VALUE +#undef FIRMWARE_VERSION + if (!retval) { + if (command_flags & COMMAND_SETCOLOURPARAMS) { + /* Adjust cam->vp to reflect these changes */ + cam->vp.brightness = + new_params.colourParams.brightness*65535/100; + cam->vp.contrast = + new_params.colourParams.contrast*65535/100; + cam->vp.colour = + new_params.colourParams.saturation*65535/100; + } + if((command_flags & COMMAND_SETEXPOSURE) && + new_params.exposure.expMode == 2) + cam->exposure_status = EXPOSURE_NORMAL; + + memcpy(&cam->params, &new_params, sizeof(struct cam_params)); + cam->mainsFreq = new_mains; + cam->cmd_queue |= command_flags; + retval = size; + } else + DBG("error: %d\n", retval); + + mutex_unlock(&cam->param_lock); + +out: + free_page((unsigned long)page); + return retval; +} + +static const struct file_operations cpia_proc_fops = { + .owner = THIS_MODULE, + .open = cpia_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = cpia_proc_write, +}; + +static void create_proc_cpia_cam(struct cam_data *cam) +{ + struct proc_dir_entry *ent; + + if (!cpia_proc_root || !cam) + return; + + ent = proc_create_data(video_device_node_name(&cam->vdev), + S_IRUGO|S_IWUSR, cpia_proc_root, + &cpia_proc_fops, cam); + if (!ent) + return; + + /* + size of the proc entry is 3736 bytes for the standard webcam; + the extra features of the QX3 microscope add 189 bytes. + (we have not yet probed the camera to see which type it is). + */ + ent->size = 3736 + 189; + cam->proc_entry = ent; +} + +static void destroy_proc_cpia_cam(struct cam_data *cam) +{ + if (!cam || !cam->proc_entry) + return; + + remove_proc_entry(video_device_node_name(&cam->vdev), cpia_proc_root); + cam->proc_entry = NULL; +} + +static void proc_cpia_create(void) +{ + cpia_proc_root = proc_mkdir("cpia", NULL); + + if (!cpia_proc_root) + LOG("Unable to initialise /proc/cpia\n"); +} + +static void __exit proc_cpia_destroy(void) +{ + remove_proc_entry("cpia", NULL); +} +#endif /* CONFIG_PROC_FS */ + +/* ----------------------- debug functions ---------------------- */ + +#define printstatus(cam) \ + DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\ + cam->params.status.systemState, cam->params.status.grabState, \ + cam->params.status.streamState, cam->params.status.fatalError, \ + cam->params.status.cmdError, cam->params.status.debugFlags, \ + cam->params.status.vpStatus, cam->params.status.errorCode); + +/* ----------------------- v4l helpers -------------------------- */ + +/* supported frame palettes and depths */ +static inline int valid_mode(u16 palette, u16 depth) +{ + if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) || + (palette == VIDEO_PALETTE_YUYV && depth == 16)) + return 1; + + if (colorspace_conv) + return (palette == VIDEO_PALETTE_GREY && depth == 8) || + (palette == VIDEO_PALETTE_RGB555 && depth == 16) || + (palette == VIDEO_PALETTE_RGB565 && depth == 16) || + (palette == VIDEO_PALETTE_RGB24 && depth == 24) || + (palette == VIDEO_PALETTE_RGB32 && depth == 32) || + (palette == VIDEO_PALETTE_UYVY && depth == 16); + + return 0; +} + +static int match_videosize( int width, int height ) +{ + /* return the best match, where 'best' is as always + * the largest that is not bigger than what is requested. */ + if (width>=352 && height>=288) + return VIDEOSIZE_352_288; /* CIF */ + + if (width>=320 && height>=240) + return VIDEOSIZE_320_240; /* SIF */ + + if (width>=288 && height>=216) + return VIDEOSIZE_288_216; + + if (width>=256 && height>=192) + return VIDEOSIZE_256_192; + + if (width>=224 && height>=168) + return VIDEOSIZE_224_168; + + if (width>=192 && height>=144) + return VIDEOSIZE_192_144; + + if (width>=176 && height>=144) + return VIDEOSIZE_176_144; /* QCIF */ + + if (width>=160 && height>=120) + return VIDEOSIZE_160_120; /* QSIF */ + + if (width>=128 && height>=96) + return VIDEOSIZE_128_96; + + if (width>=88 && height>=72) + return VIDEOSIZE_88_72; + + if (width>=64 && height>=48) + return VIDEOSIZE_64_48; + + if (width>=48 && height>=48) + return VIDEOSIZE_48_48; + + return -1; +} + +/* these are the capture sizes we support */ +static void set_vw_size(struct cam_data *cam) +{ + /* the col/row/start/end values are the result of simple math */ + /* study the SetROI-command in cpia developers guide p 2-22 */ + /* streamStartLine is set to the recommended value in the cpia */ + /* developers guide p 3-37 */ + switch(cam->video_size) { + case VIDEOSIZE_CIF: + cam->vw.width = 352; + cam->vw.height = 288; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=0; + cam->params.roi.rowStart=0; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_SIF: + cam->vw.width = 320; + cam->vw.height = 240; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=2; + cam->params.roi.rowStart=6; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_288_216: + cam->vw.width = 288; + cam->vw.height = 216; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=4; + cam->params.roi.rowStart=9; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_256_192: + cam->vw.width = 256; + cam->vw.height = 192; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=6; + cam->params.roi.rowStart=12; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_224_168: + cam->vw.width = 224; + cam->vw.height = 168; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=8; + cam->params.roi.rowStart=15; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_192_144: + cam->vw.width = 192; + cam->vw.height = 144; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=10; + cam->params.roi.rowStart=18; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_QCIF: + cam->vw.width = 176; + cam->vw.height = 144; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=0; + cam->params.roi.rowStart=0; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_QSIF: + cam->vw.width = 160; + cam->vw.height = 120; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=1; + cam->params.roi.rowStart=3; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_128_96: + cam->vw.width = 128; + cam->vw.height = 96; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=3; + cam->params.roi.rowStart=6; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_88_72: + cam->vw.width = 88; + cam->vw.height = 72; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=5; + cam->params.roi.rowStart=9; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_64_48: + cam->vw.width = 64; + cam->vw.height = 48; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=7; + cam->params.roi.rowStart=12; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_48_48: + cam->vw.width = 48; + cam->vw.height = 48; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=8; + cam->params.roi.rowStart=6; + cam->params.streamStartLine = 60; + break; + default: + LOG("bad videosize value: %d\n", cam->video_size); + return; + } + + if(cam->vc.width == 0) + cam->vc.width = cam->vw.width; + if(cam->vc.height == 0) + cam->vc.height = cam->vw.height; + + cam->params.roi.colStart += cam->vc.x >> 3; + cam->params.roi.colEnd = cam->params.roi.colStart + + (cam->vc.width >> 3); + cam->params.roi.rowStart += cam->vc.y >> 2; + cam->params.roi.rowEnd = cam->params.roi.rowStart + + (cam->vc.height >> 2); + + return; +} + +static int allocate_frame_buf(struct cam_data *cam) +{ + int i; + + cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE); + if (!cam->frame_buf) + return -ENOBUFS; + + for (i = 0; i < FRAME_NUM; i++) + cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE; + + return 0; +} + +static int free_frame_buf(struct cam_data *cam) +{ + int i; + + rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE); + cam->frame_buf = NULL; + for (i=0; i < FRAME_NUM; i++) + cam->frame[i].data = NULL; + + return 0; +} + + +static inline void free_frames(struct cpia_frame frame[FRAME_NUM]) +{ + int i; + + for (i=0; i < FRAME_NUM; i++) + frame[i].state = FRAME_UNUSED; + return; +} + +/********************************************************************** + * + * General functions + * + **********************************************************************/ +/* send an arbitrary command to the camera */ +static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d) +{ + int retval, datasize; + u8 cmd[8], data[8]; + + switch(command) { + case CPIA_COMMAND_GetCPIAVersion: + case CPIA_COMMAND_GetPnPID: + case CPIA_COMMAND_GetCameraStatus: + case CPIA_COMMAND_GetVPVersion: + datasize=8; + break; + case CPIA_COMMAND_GetColourParams: + case CPIA_COMMAND_GetColourBalance: + case CPIA_COMMAND_GetExposure: + mutex_lock(&cam->param_lock); + datasize=8; + break; + case CPIA_COMMAND_ReadMCPorts: + case CPIA_COMMAND_ReadVCRegs: + datasize = 4; + break; + default: + datasize=0; + break; + } + + cmd[0] = command>>8; + cmd[1] = command&0xff; + cmd[2] = a; + cmd[3] = b; + cmd[4] = c; + cmd[5] = d; + cmd[6] = datasize; + cmd[7] = 0; + + retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); + if (retval) { + DBG("%x - failed, retval=%d\n", command, retval); + if (command == CPIA_COMMAND_GetColourParams || + command == CPIA_COMMAND_GetColourBalance || + command == CPIA_COMMAND_GetExposure) + mutex_unlock(&cam->param_lock); + } else { + switch(command) { + case CPIA_COMMAND_GetCPIAVersion: + cam->params.version.firmwareVersion = data[0]; + cam->params.version.firmwareRevision = data[1]; + cam->params.version.vcVersion = data[2]; + cam->params.version.vcRevision = data[3]; + break; + case CPIA_COMMAND_GetPnPID: + cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8); + cam->params.pnpID.product = data[2]+(((u16)data[3])<<8); + cam->params.pnpID.deviceRevision = + data[4]+(((u16)data[5])<<8); + break; + case CPIA_COMMAND_GetCameraStatus: + cam->params.status.systemState = data[0]; + cam->params.status.grabState = data[1]; + cam->params.status.streamState = data[2]; + cam->params.status.fatalError = data[3]; + cam->params.status.cmdError = data[4]; + cam->params.status.debugFlags = data[5]; + cam->params.status.vpStatus = data[6]; + cam->params.status.errorCode = data[7]; + break; + case CPIA_COMMAND_GetVPVersion: + cam->params.vpVersion.vpVersion = data[0]; + cam->params.vpVersion.vpRevision = data[1]; + cam->params.vpVersion.cameraHeadID = + data[2]+(((u16)data[3])<<8); + break; + case CPIA_COMMAND_GetColourParams: + cam->params.colourParams.brightness = data[0]; + cam->params.colourParams.contrast = data[1]; + cam->params.colourParams.saturation = data[2]; + mutex_unlock(&cam->param_lock); + break; + case CPIA_COMMAND_GetColourBalance: + cam->params.colourBalance.redGain = data[0]; + cam->params.colourBalance.greenGain = data[1]; + cam->params.colourBalance.blueGain = data[2]; + mutex_unlock(&cam->param_lock); + break; + case CPIA_COMMAND_GetExposure: + cam->params.exposure.gain = data[0]; + cam->params.exposure.fineExp = data[1]; + cam->params.exposure.coarseExpLo = data[2]; + cam->params.exposure.coarseExpHi = data[3]; + cam->params.exposure.redComp = data[4]; + cam->params.exposure.green1Comp = data[5]; + cam->params.exposure.green2Comp = data[6]; + cam->params.exposure.blueComp = data[7]; + mutex_unlock(&cam->param_lock); + break; + + case CPIA_COMMAND_ReadMCPorts: + if (!cam->params.qx3.qx3_detected) + break; + /* test button press */ + cam->params.qx3.button = ((data[1] & 0x02) == 0); + if (cam->params.qx3.button) { + /* button pressed - unlock the latch */ + do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0); + do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0); + } + + /* test whether microscope is cradled */ + cam->params.qx3.cradled = ((data[2] & 0x40) == 0); + break; + + default: + break; + } + } + return retval; +} + +/* send a command to the camera with an additional data transaction */ +static int do_command_extended(struct cam_data *cam, u16 command, + u8 a, u8 b, u8 c, u8 d, + u8 e, u8 f, u8 g, u8 h, + u8 i, u8 j, u8 k, u8 l) +{ + int retval; + u8 cmd[8], data[8]; + + cmd[0] = command>>8; + cmd[1] = command&0xff; + cmd[2] = a; + cmd[3] = b; + cmd[4] = c; + cmd[5] = d; + cmd[6] = 8; + cmd[7] = 0; + data[0] = e; + data[1] = f; + data[2] = g; + data[3] = h; + data[4] = i; + data[5] = j; + data[6] = k; + data[7] = l; + + retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); + if (retval) + DBG("%x - failed\n", command); + + return retval; +} + +/********************************************************************** + * + * Colorspace conversion + * + **********************************************************************/ +#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) + +static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt, + int linesize, int mmap_kludge) +{ + int y, u, v, r, g, b, y1; + + /* Odd lines use the same u and v as the previous line. + * Because of compression, it is necessary to get this + * information from the decoded image. */ + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + r = ((*(rgb+1-linesize)) & 0x7c) << 1; + g = ((*(rgb-linesize)) & 0xe0) >> 4 | + ((*(rgb+1-linesize)) & 0x03) << 6; + b = ((*(rgb-linesize)) & 0x1f) << 3; + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u - 53294 * v; + b = 132278 * u; + *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); + *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); + *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); + *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); + return 4; + case VIDEO_PALETTE_RGB565: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + r = (*(rgb+1-linesize)) & 0xf8; + g = ((*(rgb-linesize)) & 0xe0) >> 3 | + ((*(rgb+1-linesize)) & 0x07) << 5; + b = ((*(rgb-linesize)) & 0x1f) << 3; + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u - 53294 * v; + b = 132278 * u; + *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); + *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); + *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); + *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); + return 4; + break; + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + if (mmap_kludge) { + r = *(rgb+2-linesize); + g = *(rgb+1-linesize); + b = *(rgb-linesize); + } else { + r = *(rgb-linesize); + g = *(rgb+1-linesize); + b = *(rgb+2-linesize); + } + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + if(out_fmt == VIDEO_PALETTE_RGB32) + rgb++; + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + if(out_fmt == VIDEO_PALETTE_RGB32) + rgb++; + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + if(out_fmt == VIDEO_PALETTE_RGB32) + return 8; + return 6; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + y = *yuv++; + u = *(rgb+1-linesize); + y1 = *yuv; + v = *(rgb+3-linesize); + *rgb++ = y; + *rgb++ = u; + *rgb++ = y1; + *rgb = v; + return 4; + case VIDEO_PALETTE_UYVY: + u = *(rgb-linesize); + y = *yuv++; + v = *(rgb+2-linesize); + y1 = *yuv; + *rgb++ = u; + *rgb++ = y; + *rgb++ = v; + *rgb = y1; + return 4; + case VIDEO_PALETTE_GREY: + *rgb++ = *yuv++; + *rgb = *yuv; + return 2; + default: + DBG("Empty: %d\n", out_fmt); + return 0; + } +} + + +static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt, + int in_uyvy, int mmap_kludge) +{ + int y, u, v, r, g, b, y1; + + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + if (in_uyvy) { + u = *yuv++ - 128; + y = (*yuv++ - 16) * 76310; + v = *yuv++ - 128; + y1 = (*yuv - 16) * 76310; + } else { + y = (*yuv++ - 16) * 76310; + u = *yuv++ - 128; + y1 = (*yuv++ - 16) * 76310; + v = *yuv - 128; + } + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + break; + default: + y = *yuv++; + u = *yuv++; + y1 = *yuv++; + v = *yuv; + /* Just to avoid compiler warnings */ + r = 0; + g = 0; + b = 0; + break; + } + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); + *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); + *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); + *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); + return 4; + case VIDEO_PALETTE_RGB565: + *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); + *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); + *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); + *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); + return 4; + case VIDEO_PALETTE_RGB24: + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + return 6; + case VIDEO_PALETTE_RGB32: + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + rgb++; + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + rgb++; + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + return 8; + case VIDEO_PALETTE_GREY: + *rgb++ = y; + *rgb = y1; + return 2; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + *rgb++ = y; + *rgb++ = u; + *rgb++ = y1; + *rgb = v; + return 4; + case VIDEO_PALETTE_UYVY: + *rgb++ = u; + *rgb++ = y; + *rgb++ = v; + *rgb = y1; + return 4; + default: + DBG("Empty: %d\n", out_fmt); + return 0; + } +} + +static int skipcount(int count, int fmt) +{ + switch(fmt) { + case VIDEO_PALETTE_GREY: + return count; + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_UYVY: + return 2*count; + case VIDEO_PALETTE_RGB24: + return 3*count; + case VIDEO_PALETTE_RGB32: + return 4*count; + default: + return 0; + } +} + +static int parse_picture(struct cam_data *cam, int size) +{ + u8 *obuf, *ibuf, *end_obuf; + int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt; + int rows, cols, linesize, subsample_422; + + /* make sure params don't change while we are decoding */ + mutex_lock(&cam->param_lock); + + obuf = cam->decompressed_frame.data; + end_obuf = obuf+CPIA_MAX_FRAME_SIZE; + ibuf = cam->raw_image; + origsize = size; + out_fmt = cam->vp.palette; + + if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) { + LOG("header not found\n"); + mutex_unlock(&cam->param_lock); + return -1; + } + + if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) { + LOG("wrong video size\n"); + mutex_unlock(&cam->param_lock); + return -1; + } + + if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) { + LOG("illegal subtype %d\n",ibuf[17]); + mutex_unlock(&cam->param_lock); + return -1; + } + subsample_422 = ibuf[17] == SUBSAMPLE_422; + + if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) { + LOG("illegal yuvorder %d\n",ibuf[18]); + mutex_unlock(&cam->param_lock); + return -1; + } + in_uyvy = ibuf[18] == YUVORDER_UYVY; + + if ((ibuf[24] != cam->params.roi.colStart) || + (ibuf[25] != cam->params.roi.colEnd) || + (ibuf[26] != cam->params.roi.rowStart) || + (ibuf[27] != cam->params.roi.rowEnd)) { + LOG("ROI mismatch\n"); + mutex_unlock(&cam->param_lock); + return -1; + } + cols = 8*(ibuf[25] - ibuf[24]); + rows = 4*(ibuf[27] - ibuf[26]); + + + if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) { + LOG("illegal compression %d\n",ibuf[28]); + mutex_unlock(&cam->param_lock); + return -1; + } + compressed = (ibuf[28] == COMPRESSED); + + if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) { + LOG("illegal decimation %d\n",ibuf[29]); + mutex_unlock(&cam->param_lock); + return -1; + } + decimation = (ibuf[29] == DECIMATION_ENAB); + + cam->params.yuvThreshold.yThreshold = ibuf[30]; + cam->params.yuvThreshold.uvThreshold = ibuf[31]; + cam->params.status.systemState = ibuf[32]; + cam->params.status.grabState = ibuf[33]; + cam->params.status.streamState = ibuf[34]; + cam->params.status.fatalError = ibuf[35]; + cam->params.status.cmdError = ibuf[36]; + cam->params.status.debugFlags = ibuf[37]; + cam->params.status.vpStatus = ibuf[38]; + cam->params.status.errorCode = ibuf[39]; + cam->fps = ibuf[41]; + mutex_unlock(&cam->param_lock); + + linesize = skipcount(cols, out_fmt); + ibuf += FRAME_HEADER_SIZE; + size -= FRAME_HEADER_SIZE; + ll = ibuf[0] | (ibuf[1] << 8); + ibuf += 2; + even_line = 1; + + while (size > 0) { + size -= (ll+2); + if (size < 0) { + LOG("Insufficient data in buffer\n"); + return -1; + } + + while (ll > 1) { + if (!compressed || (compressed && !(*ibuf & 1))) { + if(subsample_422 || even_line) { + obuf += yuvconvert(ibuf, obuf, out_fmt, + in_uyvy, cam->mmap_kludge); + ibuf += 4; + ll -= 4; + } else { + /* SUBSAMPLE_420 on an odd line */ + obuf += convert420(ibuf, obuf, + out_fmt, linesize, + cam->mmap_kludge); + ibuf += 2; + ll -= 2; + } + } else { + /*skip compressed interval from previous frame*/ + obuf += skipcount(*ibuf >> 1, out_fmt); + if (obuf > end_obuf) { + LOG("Insufficient buffer size\n"); + return -1; + } + ++ibuf; + ll--; + } + } + if (ll == 1) { + if (*ibuf != EOL) { + DBG("EOL not found giving up after %d/%d" + " bytes\n", origsize-size, origsize); + return -1; + } + + ++ibuf; /* skip over EOL */ + + if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) && + (ibuf[2] == EOI) && (ibuf[3] == EOI)) { + size -= 4; + break; + } + + if(decimation) { + /* skip the odd lines for now */ + obuf += linesize; + } + + if (size > 1) { + ll = ibuf[0] | (ibuf[1] << 8); + ibuf += 2; /* skip over line length */ + } + if(!decimation) + even_line = !even_line; + } else { + LOG("line length was not 1 but %d after %d/%d bytes\n", + ll, origsize-size, origsize); + return -1; + } + } + + if(decimation) { + /* interpolate odd rows */ + int i, j; + u8 *prev, *next; + prev = cam->decompressed_frame.data; + obuf = prev+linesize; + next = obuf+linesize; + for(i=1; idecompressed_frame.count = obuf-cam->decompressed_frame.data; + + return cam->decompressed_frame.count; +} + +/* InitStreamCap wrapper to select correct start line */ +static inline int init_stream_cap(struct cam_data *cam) +{ + return do_command(cam, CPIA_COMMAND_InitStreamCap, + 0, cam->params.streamStartLine, 0, 0); +} + + +/* find_over_exposure + * Finds a suitable value of OverExposure for use with SetFlickerCtrl + * Some calculation is required because this value changes with the brightness + * set with SetColourParameters + * + * Parameters: Brightness - last brightness value set with SetColourParameters + * + * Returns: OverExposure value to use with SetFlickerCtrl + */ +#define FLICKER_MAX_EXPOSURE 250 +#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146 +#define FLICKER_BRIGHTNESS_CONSTANT 59 +static int find_over_exposure(int brightness) +{ + int MaxAllowableOverExposure, OverExposure; + + MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness - + FLICKER_BRIGHTNESS_CONSTANT; + + if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) { + OverExposure = MaxAllowableOverExposure; + } else { + OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE; + } + + return OverExposure; +} +#undef FLICKER_MAX_EXPOSURE +#undef FLICKER_ALLOWABLE_OVER_EXPOSURE +#undef FLICKER_BRIGHTNESS_CONSTANT + +/* update various camera modes and settings */ +static void dispatch_commands(struct cam_data *cam) +{ + mutex_lock(&cam->param_lock); + if (cam->cmd_queue==COMMAND_NONE) { + mutex_unlock(&cam->param_lock); + return; + } + DEB_BYTE(cam->cmd_queue); + DEB_BYTE(cam->cmd_queue>>8); + if (cam->cmd_queue & COMMAND_SETFORMAT) { + do_command(cam, CPIA_COMMAND_SetFormat, + cam->params.format.videoSize, + cam->params.format.subSample, + cam->params.format.yuvOrder, 0); + do_command(cam, CPIA_COMMAND_SetROI, + cam->params.roi.colStart, cam->params.roi.colEnd, + cam->params.roi.rowStart, cam->params.roi.rowEnd); + cam->first_frame = 1; + } + + if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS) + do_command(cam, CPIA_COMMAND_SetColourParams, + cam->params.colourParams.brightness, + cam->params.colourParams.contrast, + cam->params.colourParams.saturation, 0); + + if (cam->cmd_queue & COMMAND_SETAPCOR) + do_command(cam, CPIA_COMMAND_SetApcor, + cam->params.apcor.gain1, + cam->params.apcor.gain2, + cam->params.apcor.gain4, + cam->params.apcor.gain8); + + if (cam->cmd_queue & COMMAND_SETVLOFFSET) + do_command(cam, CPIA_COMMAND_SetVLOffset, + cam->params.vlOffset.gain1, + cam->params.vlOffset.gain2, + cam->params.vlOffset.gain4, + cam->params.vlOffset.gain8); + + if (cam->cmd_queue & COMMAND_SETEXPOSURE) { + do_command_extended(cam, CPIA_COMMAND_SetExposure, + cam->params.exposure.gainMode, + 1, + cam->params.exposure.compMode, + cam->params.exposure.centreWeight, + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + if(cam->params.exposure.expMode != 1) { + do_command_extended(cam, CPIA_COMMAND_SetExposure, + 0, + cam->params.exposure.expMode, + 0, 0, + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + 0, 0, 0, 0); + } + } + + if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) { + if (cam->params.colourBalance.balanceMode == 1) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 1, + cam->params.colourBalance.redGain, + cam->params.colourBalance.greenGain, + cam->params.colourBalance.blueGain); + do_command(cam, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); + } + if (cam->params.colourBalance.balanceMode == 2) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 2, 0, 0, 0); + } + if (cam->params.colourBalance.balanceMode == 3) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); + } + } + + if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET) + do_command(cam, CPIA_COMMAND_SetCompressionTarget, + cam->params.compressionTarget.frTargeting, + cam->params.compressionTarget.targetFR, + cam->params.compressionTarget.targetQ, 0); + + if (cam->cmd_queue & COMMAND_SETYUVTHRESH) + do_command(cam, CPIA_COMMAND_SetYUVThresh, + cam->params.yuvThreshold.yThreshold, + cam->params.yuvThreshold.uvThreshold, 0, 0); + + if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS) + do_command_extended(cam, CPIA_COMMAND_SetCompressionParams, + 0, 0, 0, 0, + cam->params.compressionParams.hysteresis, + cam->params.compressionParams.threshMax, + cam->params.compressionParams.smallStep, + cam->params.compressionParams.largeStep, + cam->params.compressionParams.decimationHysteresis, + cam->params.compressionParams.frDiffStepThresh, + cam->params.compressionParams.qDiffStepThresh, + cam->params.compressionParams.decimationThreshMod); + + if (cam->cmd_queue & COMMAND_SETCOMPRESSION) + do_command(cam, CPIA_COMMAND_SetCompression, + cam->params.compression.mode, + cam->params.compression.decimation, 0, 0); + + if (cam->cmd_queue & COMMAND_SETSENSORFPS) + do_command(cam, CPIA_COMMAND_SetSensorFPS, + cam->params.sensorFps.divisor, + cam->params.sensorFps.baserate, 0, 0); + + if (cam->cmd_queue & COMMAND_SETFLICKERCTRL) + do_command(cam, CPIA_COMMAND_SetFlickerCtrl, + cam->params.flickerControl.flickerMode, + cam->params.flickerControl.coarseJump, + abs(cam->params.flickerControl.allowableOverExposure), + 0); + + if (cam->cmd_queue & COMMAND_SETECPTIMING) + do_command(cam, CPIA_COMMAND_SetECPTiming, + cam->params.ecpTiming, 0, 0, 0); + + if (cam->cmd_queue & COMMAND_PAUSE) + do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); + + if (cam->cmd_queue & COMMAND_RESUME) + init_stream_cap(cam); + + if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected) + { + int p1 = (cam->params.qx3.bottomlight == 0) << 1; + int p2 = (cam->params.qx3.toplight == 0) << 3; + do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0); + do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0); + } + + cam->cmd_queue = COMMAND_NONE; + mutex_unlock(&cam->param_lock); + return; +} + + + +static void set_flicker(struct cam_params *params, volatile u32 *command_flags, + int on) +{ + /* Everything in here is from the Windows driver */ +#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \ + params->version.firmwareRevision == (y)) +/* define for compgain calculation */ +#if 0 +#define COMPGAIN(base, curexp, newexp) \ + (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5) +#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ + (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128)) +#else + /* equivalent functions without floating point math */ +#define COMPGAIN(base, curexp, newexp) \ + (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) ) +#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ + (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128))) +#endif + + + int currentexp = params->exposure.coarseExpLo + + params->exposure.coarseExpHi*256; + int startexp; + if (on) { + int cj = params->flickerControl.coarseJump; + params->flickerControl.flickerMode = 1; + params->flickerControl.disabled = 0; + if(params->exposure.expMode != 2) + *command_flags |= COMMAND_SETEXPOSURE; + params->exposure.expMode = 2; + currentexp = currentexp << params->exposure.gain; + params->exposure.gain = 0; + /* round down current exposure to nearest value */ + startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj; + if(startexp < 1) + startexp = 1; + startexp = (startexp * cj) - 1; + if(FIRMWARE_VERSION(1,2)) + while(startexp > MAX_EXP_102) + startexp -= cj; + else + while(startexp > MAX_EXP) + startexp -= cj; + params->exposure.coarseExpLo = startexp & 0xff; + params->exposure.coarseExpHi = startexp >> 8; + if (currentexp > startexp) { + if (currentexp > (2 * startexp)) + currentexp = 2 * startexp; + params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp); + params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp); + params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp); + params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp); + } else { + params->exposure.redComp = COMP_RED; + params->exposure.green1Comp = COMP_GREEN1; + params->exposure.green2Comp = COMP_GREEN2; + params->exposure.blueComp = COMP_BLUE; + } + if(FIRMWARE_VERSION(1,2)) + params->exposure.compMode = 0; + else + params->exposure.compMode = 1; + + params->apcor.gain1 = 0x18; + params->apcor.gain2 = 0x18; + params->apcor.gain4 = 0x16; + params->apcor.gain8 = 0x14; + *command_flags |= COMMAND_SETAPCOR; + } else { + params->flickerControl.flickerMode = 0; + params->flickerControl.disabled = 1; + /* Coarse = average of equivalent coarse for each comp channel */ + startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp); + startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp); + startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp); + startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp); + startexp = startexp >> 2; + while(startexp > MAX_EXP && + params->exposure.gain < params->exposure.gainMode-1) { + startexp = startexp >> 1; + ++params->exposure.gain; + } + if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102) + startexp = MAX_EXP_102; + if(startexp > MAX_EXP) + startexp = MAX_EXP; + params->exposure.coarseExpLo = startexp&0xff; + params->exposure.coarseExpHi = startexp >> 8; + params->exposure.redComp = COMP_RED; + params->exposure.green1Comp = COMP_GREEN1; + params->exposure.green2Comp = COMP_GREEN2; + params->exposure.blueComp = COMP_BLUE; + params->exposure.compMode = 1; + *command_flags |= COMMAND_SETEXPOSURE; + params->apcor.gain1 = 0x18; + params->apcor.gain2 = 0x16; + params->apcor.gain4 = 0x24; + params->apcor.gain8 = 0x34; + *command_flags |= COMMAND_SETAPCOR; + } + params->vlOffset.gain1 = 20; + params->vlOffset.gain2 = 24; + params->vlOffset.gain4 = 26; + params->vlOffset.gain8 = 26; + *command_flags |= COMMAND_SETVLOFFSET; +#undef FIRMWARE_VERSION +#undef EXP_FROM_COMP +#undef COMPGAIN +} + +#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \ + cam->params.version.firmwareRevision == (y)) +/* monitor the exposure and adjust the sensor frame rate if needed */ +static void monitor_exposure(struct cam_data *cam) +{ + u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8]; + int retval, light_exp, dark_exp, very_dark_exp; + int old_exposure, new_exposure, framerate; + + /* get necessary stats and register settings from camera */ + /* do_command can't handle this, so do it ourselves */ + cmd[0] = CPIA_COMMAND_ReadVPRegs>>8; + cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff; + cmd[2] = 30; + cmd[3] = 4; + cmd[4] = 9; + cmd[5] = 8; + cmd[6] = 8; + cmd[7] = 0; + retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); + if (retval) { + LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n", + retval); + return; + } + exp_acc = data[0]; + bcomp = data[1]; + gain = data[2]; + coarseL = data[3]; + + mutex_lock(&cam->param_lock); + light_exp = cam->params.colourParams.brightness + + TC - 50 + EXP_ACC_LIGHT; + if(light_exp > 255) + light_exp = 255; + dark_exp = cam->params.colourParams.brightness + + TC - 50 - EXP_ACC_DARK; + if(dark_exp < 0) + dark_exp = 0; + very_dark_exp = dark_exp/2; + + old_exposure = cam->params.exposure.coarseExpHi * 256 + + cam->params.exposure.coarseExpLo; + + if(!cam->params.flickerControl.disabled) { + /* Flicker control on */ + int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102; + bcomp += 128; /* decode */ + if(bcomp >= max_comp && exp_acc < dark_exp) { + /* dark */ + if(exp_acc < very_dark_exp) { + /* very dark */ + if(cam->exposure_status == EXPOSURE_VERY_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_DARK; + cam->exposure_count = 1; + } + } else { + /* just dark */ + if(cam->exposure_status == EXPOSURE_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_DARK; + cam->exposure_count = 1; + } + } + } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { + /* light */ + if(old_exposure <= VERY_LOW_EXP) { + /* very light */ + if(cam->exposure_status == EXPOSURE_VERY_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_LIGHT; + cam->exposure_count = 1; + } + } else { + /* just light */ + if(cam->exposure_status == EXPOSURE_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_LIGHT; + cam->exposure_count = 1; + } + } + } else { + /* not dark or light */ + cam->exposure_status = EXPOSURE_NORMAL; + } + } else { + /* Flicker control off */ + if(old_exposure >= MAX_EXP && exp_acc < dark_exp) { + /* dark */ + if(exp_acc < very_dark_exp) { + /* very dark */ + if(cam->exposure_status == EXPOSURE_VERY_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_DARK; + cam->exposure_count = 1; + } + } else { + /* just dark */ + if(cam->exposure_status == EXPOSURE_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_DARK; + cam->exposure_count = 1; + } + } + } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { + /* light */ + if(old_exposure <= VERY_LOW_EXP) { + /* very light */ + if(cam->exposure_status == EXPOSURE_VERY_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_LIGHT; + cam->exposure_count = 1; + } + } else { + /* just light */ + if(cam->exposure_status == EXPOSURE_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_LIGHT; + cam->exposure_count = 1; + } + } + } else { + /* not dark or light */ + cam->exposure_status = EXPOSURE_NORMAL; + } + } + + framerate = cam->fps; + if(framerate > 30 || framerate < 1) + framerate = 1; + + if(!cam->params.flickerControl.disabled) { + /* Flicker control on */ + if((cam->exposure_status == EXPOSURE_VERY_DARK || + cam->exposure_status == EXPOSURE_DARK) && + cam->exposure_count >= DARK_TIME*framerate && + cam->params.sensorFps.divisor < 3) { + + /* dark for too long */ + ++cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + cam->params.flickerControl.coarseJump = + flicker_jumps[cam->mainsFreq] + [cam->params.sensorFps.baserate] + [cam->params.sensorFps.divisor]; + cam->cmd_queue |= COMMAND_SETFLICKERCTRL; + + new_exposure = cam->params.flickerControl.coarseJump-1; + while(new_exposure < old_exposure/2) + new_exposure += cam->params.flickerControl.coarseJump; + cam->params.exposure.coarseExpLo = new_exposure & 0xff; + cam->params.exposure.coarseExpHi = new_exposure >> 8; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically decreasing sensor_fps\n"); + + } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || + cam->exposure_status == EXPOSURE_LIGHT) && + cam->exposure_count >= LIGHT_TIME*framerate && + cam->params.sensorFps.divisor > 0) { + + /* light for too long */ + int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ; + + --cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + cam->params.flickerControl.coarseJump = + flicker_jumps[cam->mainsFreq] + [cam->params.sensorFps.baserate] + [cam->params.sensorFps.divisor]; + cam->cmd_queue |= COMMAND_SETFLICKERCTRL; + + new_exposure = cam->params.flickerControl.coarseJump-1; + while(new_exposure < 2*old_exposure && + new_exposure+ + cam->params.flickerControl.coarseJump < max_exp) + new_exposure += cam->params.flickerControl.coarseJump; + cam->params.exposure.coarseExpLo = new_exposure & 0xff; + cam->params.exposure.coarseExpHi = new_exposure >> 8; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically increasing sensor_fps\n"); + } + } else { + /* Flicker control off */ + if((cam->exposure_status == EXPOSURE_VERY_DARK || + cam->exposure_status == EXPOSURE_DARK) && + cam->exposure_count >= DARK_TIME*framerate && + cam->params.sensorFps.divisor < 3) { + + /* dark for too long */ + ++cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + if(cam->params.exposure.gain > 0) { + --cam->params.exposure.gain; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + } + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically decreasing sensor_fps\n"); + + } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || + cam->exposure_status == EXPOSURE_LIGHT) && + cam->exposure_count >= LIGHT_TIME*framerate && + cam->params.sensorFps.divisor > 0) { + + /* light for too long */ + --cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + if(cam->params.exposure.gain < + cam->params.exposure.gainMode-1) { + ++cam->params.exposure.gain; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + } + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically increasing sensor_fps\n"); + } + } + mutex_unlock(&cam->param_lock); +} + +/*-----------------------------------------------------------------*/ +/* if flicker is switched off, this function switches it back on.It checks, + however, that conditions are suitable before restarting it. + This should only be called for firmware version 1.2. + + It also adjust the colour balance when an exposure step is detected - as + long as flicker is running +*/ +static void restart_flicker(struct cam_data *cam) +{ + int cam_exposure, old_exp; + if(!FIRMWARE_VERSION(1,2)) + return; + mutex_lock(&cam->param_lock); + if(cam->params.flickerControl.flickerMode == 0 || + cam->raw_image[39] == 0) { + mutex_unlock(&cam->param_lock); + return; + } + cam_exposure = cam->raw_image[39]*2; + old_exp = cam->params.exposure.coarseExpLo + + cam->params.exposure.coarseExpHi*256; + /* + see how far away camera exposure is from a valid + flicker exposure value + */ + cam_exposure %= cam->params.flickerControl.coarseJump; + if(!cam->params.flickerControl.disabled && + cam_exposure <= cam->params.flickerControl.coarseJump - 3) { + /* Flicker control auto-disabled */ + cam->params.flickerControl.disabled = 1; + } + + if(cam->params.flickerControl.disabled && + cam->params.flickerControl.flickerMode && + old_exp > cam->params.flickerControl.coarseJump + + ROUND_UP_EXP_FOR_FLICKER) { + /* exposure is now high enough to switch + flicker control back on */ + set_flicker(&cam->params, &cam->cmd_queue, 1); + if((cam->cmd_queue & COMMAND_SETEXPOSURE) && + cam->params.exposure.expMode == 2) + cam->exposure_status = EXPOSURE_NORMAL; + + } + mutex_unlock(&cam->param_lock); +} +#undef FIRMWARE_VERSION + +static int clear_stall(struct cam_data *cam) +{ + /* FIXME: Does this actually work? */ + LOG("Clearing stall\n"); + + cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0); + do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); + return cam->params.status.streamState != STREAM_PAUSED; +} + +/* kernel thread function to read image from camera */ +static int fetch_frame(void *data) +{ + int image_size, retry; + struct cam_data *cam = (struct cam_data *)data; + unsigned long oldjif, rate, diff; + + /* Allow up to two bad images in a row to be read and + * ignored before an error is reported */ + for (retry = 0; retry < 3; ++retry) { + if (retry) + DBG("retry=%d\n", retry); + + if (!cam->ops) + continue; + + /* load first frame always uncompressed */ + if (cam->first_frame && + cam->params.compression.mode != CPIA_COMPRESSION_NONE) { + do_command(cam, CPIA_COMMAND_SetCompression, + CPIA_COMPRESSION_NONE, + NO_DECIMATION, 0, 0); + /* Trial & error - Discarding a frame prevents the + first frame from having an error in the data. */ + do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); + } + + /* init camera upload */ + if (do_command(cam, CPIA_COMMAND_GrabFrame, 0, + cam->params.streamStartLine, 0, 0)) + continue; + + if (cam->ops->wait_for_stream_ready) { + /* loop until image ready */ + int count = 0; + do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); + while (cam->params.status.streamState != STREAM_READY) { + if(++count > READY_TIMEOUT) + break; + if(cam->params.status.streamState == + STREAM_PAUSED) { + /* Bad news */ + if(!clear_stall(cam)) + return -EIO; + } + + cond_resched(); + + /* sleep for 10 ms, hopefully ;) */ + msleep_interruptible(10); + if (signal_pending(current)) + return -EINTR; + + do_command(cam, CPIA_COMMAND_GetCameraStatus, + 0, 0, 0, 0); + } + if(cam->params.status.streamState != STREAM_READY) { + continue; + } + } + + cond_resched(); + + /* grab image from camera */ + oldjif = jiffies; + image_size = cam->ops->streamRead(cam->lowlevel_data, + cam->raw_image, 0); + if (image_size <= 0) { + DBG("streamRead failed: %d\n", image_size); + continue; + } + + rate = image_size * HZ / 1024; + diff = jiffies-oldjif; + cam->transfer_rate = diff==0 ? rate : rate/diff; + /* diff==0 ? unlikely but possible */ + + /* Switch flicker control back on if it got turned off */ + restart_flicker(cam); + + /* If AEC is enabled, monitor the exposure and + adjust the sensor frame rate if needed */ + if(cam->params.exposure.expMode == 2) + monitor_exposure(cam); + + /* camera idle now so dispatch queued commands */ + dispatch_commands(cam); + + /* Update our knowledge of the camera state */ + do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); + + /* decompress and convert image to by copying it from + * raw_image to decompressed_frame + */ + + cond_resched(); + + cam->image_size = parse_picture(cam, image_size); + if (cam->image_size <= 0) { + DBG("parse_picture failed %d\n", cam->image_size); + if(cam->params.compression.mode != + CPIA_COMPRESSION_NONE) { + /* Compression may not work right if we + had a bad frame, get the next one + uncompressed. */ + cam->first_frame = 1; + do_command(cam, CPIA_COMMAND_SetGrabMode, + CPIA_GRAB_SINGLE, 0, 0, 0); + /* FIXME: Trial & error - need up to 70ms for + the grab mode change to complete ? */ + msleep_interruptible(70); + if (signal_pending(current)) + return -EINTR; + } + } else + break; + } + + if (retry < 3) { + /* FIXME: this only works for double buffering */ + if (cam->frame[cam->curframe].state == FRAME_READY) { + memcpy(cam->frame[cam->curframe].data, + cam->decompressed_frame.data, + cam->decompressed_frame.count); + cam->frame[cam->curframe].state = FRAME_DONE; + } else + cam->decompressed_frame.state = FRAME_DONE; + + if (cam->first_frame) { + cam->first_frame = 0; + do_command(cam, CPIA_COMMAND_SetCompression, + cam->params.compression.mode, + cam->params.compression.decimation, 0, 0); + + /* Switch from single-grab to continuous grab */ + do_command(cam, CPIA_COMMAND_SetGrabMode, + CPIA_GRAB_CONTINUOUS, 0, 0, 0); + } + return 0; + } + return -EIO; +} + +static int capture_frame(struct cam_data *cam, struct video_mmap *vm) +{ + if (!cam->frame_buf) { + /* we do lazy allocation */ + int err; + if ((err = allocate_frame_buf(cam))) + return err; + } + + cam->curframe = vm->frame; + cam->frame[cam->curframe].state = FRAME_READY; + return fetch_frame(cam); +} + +static int goto_high_power(struct cam_data *cam) +{ + if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) + return -EIO; + msleep_interruptible(40); /* windows driver does it too */ + if(signal_pending(current)) + return -EINTR; + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -EIO; + if (cam->params.status.systemState == HI_POWER_STATE) { + DBG("camera now in HIGH power state\n"); + return 0; + } + printstatus(cam); + return -EIO; +} + +static int goto_low_power(struct cam_data *cam) +{ + if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) + return -1; + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -1; + if (cam->params.status.systemState == LO_POWER_STATE) { + DBG("camera now in LOW power state\n"); + return 0; + } + printstatus(cam); + return -1; +} + +static void save_camera_state(struct cam_data *cam) +{ + if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE)) + do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + if(!(cam->cmd_queue & COMMAND_SETEXPOSURE)) + do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + + DBG("%d/%d/%d/%d/%d/%d/%d/%d\n", + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + DBG("%d/%d/%d\n", + cam->params.colourBalance.redGain, + cam->params.colourBalance.greenGain, + cam->params.colourBalance.blueGain); +} + +static int set_camera_state(struct cam_data *cam) +{ + cam->cmd_queue = COMMAND_SETCOMPRESSION | + COMMAND_SETCOMPRESSIONTARGET | + COMMAND_SETCOLOURPARAMS | + COMMAND_SETFORMAT | + COMMAND_SETYUVTHRESH | + COMMAND_SETECPTIMING | + COMMAND_SETCOMPRESSIONPARAMS | + COMMAND_SETEXPOSURE | + COMMAND_SETCOLOURBALANCE | + COMMAND_SETSENSORFPS | + COMMAND_SETAPCOR | + COMMAND_SETFLICKERCTRL | + COMMAND_SETVLOFFSET; + + do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0); + dispatch_commands(cam); + + /* Wait 6 frames for the sensor to get all settings and + AEC/ACB to settle */ + msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) * + (1 << cam->params.sensorFps.divisor) + 10); + + if(signal_pending(current)) + return -EINTR; + + save_camera_state(cam); + + return 0; +} + +static void get_version_information(struct cam_data *cam) +{ + /* GetCPIAVersion */ + do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0); + + /* GetPnPID */ + do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0); +} + +/* initialize camera */ +static int reset_camera(struct cam_data *cam) +{ + int err; + /* Start the camera in low power mode */ + if (goto_low_power(cam)) { + if (cam->params.status.systemState != WARM_BOOT_STATE) + return -ENODEV; + + /* FIXME: this is just dirty trial and error */ + err = goto_high_power(cam); + if(err) + return err; + do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); + if (goto_low_power(cam)) + return -ENODEV; + } + + /* procedure described in developer's guide p3-28 */ + + /* Check the firmware version. */ + cam->params.version.firmwareVersion = 0; + get_version_information(cam); + if (cam->params.version.firmwareVersion != 1) + return -ENODEV; + + /* A bug in firmware 1-02 limits gainMode to 2 */ + if(cam->params.version.firmwareRevision <= 2 && + cam->params.exposure.gainMode > 2) { + cam->params.exposure.gainMode = 2; + } + + /* set QX3 detected flag */ + cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 && + cam->params.pnpID.product == 0x0001); + + /* The fatal error checking should be done after + * the camera powers up (developer's guide p 3-38) */ + + /* Set streamState before transition to high power to avoid bug + * in firmware 1-02 */ + do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0, + STREAM_NOT_READY, 0); + + /* GotoHiPower */ + err = goto_high_power(cam); + if (err) + return err; + + /* Check the camera status */ + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -EIO; + + if (cam->params.status.fatalError) { + DBG("fatal_error: %#04x\n", + cam->params.status.fatalError); + DBG("vp_status: %#04x\n", + cam->params.status.vpStatus); + if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) { + /* Fatal error in camera */ + return -EIO; + } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) { + /* Firmware 1-02 may do this for parallel port cameras, + * just clear the flags (developer's guide p 3-38) */ + do_command(cam, CPIA_COMMAND_ModifyCameraStatus, + FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0); + } + } + + /* Check the camera status again */ + if (cam->params.status.fatalError) { + if (cam->params.status.fatalError) + return -EIO; + } + + /* VPVersion can't be retrieved before the camera is in HiPower, + * so get it here instead of in get_version_information. */ + do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0); + + /* set camera to a known state */ + return set_camera_state(cam); +} + +static void put_cam(struct cpia_camera_ops* ops) +{ + module_put(ops->owner); +} + +/* ------------------------- V4L interface --------------------- */ +static int cpia_open(struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct cam_data *cam = video_get_drvdata(dev); + int err; + + if (!cam) { + DBG("Internal error, cam_data not found!\n"); + return -ENODEV; + } + + if (cam->open_count > 0) { + DBG("Camera already open\n"); + return -EBUSY; + } + + if (!try_module_get(cam->ops->owner)) + return -ENODEV; + + mutex_lock(&cam->busy_lock); + err = -ENOMEM; + if (!cam->raw_image) { + cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE); + if (!cam->raw_image) + goto oops; + } + + if (!cam->decompressed_frame.data) { + cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE); + if (!cam->decompressed_frame.data) + goto oops; + } + + /* open cpia */ + err = -ENODEV; + if (cam->ops->open(cam->lowlevel_data)) + goto oops; + + /* reset the camera */ + if ((err = reset_camera(cam)) != 0) { + cam->ops->close(cam->lowlevel_data); + goto oops; + } + + err = -EINTR; + if(signal_pending(current)) + goto oops; + + /* Set ownership of /proc/cpia/videoX to current user */ + if(cam->proc_entry) + cam->proc_entry->uid = current_uid(); + + /* set mark for loading first frame uncompressed */ + cam->first_frame = 1; + + /* init it to something */ + cam->mmap_kludge = 0; + + ++cam->open_count; + file->private_data = dev; + mutex_unlock(&cam->busy_lock); + return 0; + + oops: + if (cam->decompressed_frame.data) { + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data = NULL; + } + if (cam->raw_image) { + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image = NULL; + } + mutex_unlock(&cam->busy_lock); + put_cam(cam->ops); + return err; +} + +static int cpia_close(struct file *file) +{ + struct video_device *dev = file->private_data; + struct cam_data *cam = video_get_drvdata(dev); + + if (cam->ops) { + /* Return ownership of /proc/cpia/videoX to root */ + if(cam->proc_entry) + cam->proc_entry->uid = 0; + + /* save camera state for later open (developers guide ch 3.5.3) */ + save_camera_state(cam); + + /* GotoLoPower */ + goto_low_power(cam); + + /* Update the camera status */ + do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + + /* cleanup internal state stuff */ + free_frames(cam->frame); + + /* close cpia */ + cam->ops->close(cam->lowlevel_data); + + put_cam(cam->ops); + } + + if (--cam->open_count == 0) { + /* clean up capture-buffers */ + if (cam->raw_image) { + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image = NULL; + } + + if (cam->decompressed_frame.data) { + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data = NULL; + } + + if (cam->frame_buf) + free_frame_buf(cam); + + if (!cam->ops) + kfree(cam); + } + file->private_data = NULL; + + return 0; +} + +static ssize_t cpia_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *dev = file->private_data; + struct cam_data *cam = video_get_drvdata(dev); + int err; + + /* make this _really_ smp and multithread-safe */ + if (mutex_lock_interruptible(&cam->busy_lock)) + return -EINTR; + + if (!buf) { + DBG("buf NULL\n"); + mutex_unlock(&cam->busy_lock); + return -EINVAL; + } + + if (!count) { + DBG("count 0\n"); + mutex_unlock(&cam->busy_lock); + return 0; + } + + if (!cam->ops) { + DBG("ops NULL\n"); + mutex_unlock(&cam->busy_lock); + return -ENODEV; + } + + /* upload frame */ + cam->decompressed_frame.state = FRAME_READY; + cam->mmap_kludge=0; + if((err = fetch_frame(cam)) != 0) { + DBG("ERROR from fetch_frame: %d\n", err); + mutex_unlock(&cam->busy_lock); + return err; + } + cam->decompressed_frame.state = FRAME_UNUSED; + + /* copy data to user space */ + if (cam->decompressed_frame.count > count) { + DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count, + (unsigned long) count); + mutex_unlock(&cam->busy_lock); + return -EFAULT; + } + if (copy_to_user(buf, cam->decompressed_frame.data, + cam->decompressed_frame.count)) { + DBG("copy_to_user failed\n"); + mutex_unlock(&cam->busy_lock); + return -EFAULT; + } + + mutex_unlock(&cam->busy_lock); + return cam->decompressed_frame.count; +} + +static long cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg) +{ + struct video_device *dev = file->private_data; + struct cam_data *cam = video_get_drvdata(dev); + int retval = 0; + + if (!cam || !cam->ops) + return -ENODEV; + + /* make this _really_ smp-safe */ + if (mutex_lock_interruptible(&cam->busy_lock)) + return -EINTR; + + /* DBG("cpia_ioctl: %u\n", cmd); */ + + switch (cmd) { + /* query capabilities */ + case VIDIOCGCAP: + { + struct video_capability *b = arg; + + DBG("VIDIOCGCAP\n"); + strcpy(b->name, "CPiA Camera"); + b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; + b->channels = 1; + b->audios = 0; + b->maxwidth = 352; /* VIDEOSIZE_CIF */ + b->maxheight = 288; + b->minwidth = 48; /* VIDEOSIZE_48_48 */ + b->minheight = 48; + break; + } + + /* get/set video source - we are a camera and nothing else */ + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + DBG("VIDIOCGCHAN\n"); + if (v->channel != 0) { + retval = -EINVAL; + break; + } + + v->channel = 0; + strcpy(v->name, "Camera"); + v->tuners = 0; + v->flags = 0; + v->type = VIDEO_TYPE_CAMERA; + v->norm = 0; + break; + } + + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + + DBG("VIDIOCSCHAN\n"); + if (v->channel != 0) + retval = -EINVAL; + break; + } + + /* image properties */ + case VIDIOCGPICT: + { + struct video_picture *pic = arg; + DBG("VIDIOCGPICT\n"); + *pic = cam->vp; + break; + } + + case VIDIOCSPICT: + { + struct video_picture *vp = arg; + + DBG("VIDIOCSPICT\n"); + + /* check validity */ + DBG("palette: %d\n", vp->palette); + DBG("depth: %d\n", vp->depth); + if (!valid_mode(vp->palette, vp->depth)) { + retval = -EINVAL; + break; + } + + mutex_lock(&cam->param_lock); + /* brightness, colour, contrast need no check 0-65535 */ + cam->vp = *vp; + /* update cam->params.colourParams */ + cam->params.colourParams.brightness = vp->brightness*100/65535; + cam->params.colourParams.contrast = vp->contrast*100/65535; + cam->params.colourParams.saturation = vp->colour*100/65535; + /* contrast is in steps of 8, so round */ + cam->params.colourParams.contrast = + ((cam->params.colourParams.contrast + 3) / 8) * 8; + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2 && + cam->params.colourParams.contrast > 80) { + /* 1-02 firmware limits contrast to 80 */ + cam->params.colourParams.contrast = 80; + } + + /* Adjust flicker control if necessary */ + if(cam->params.flickerControl.allowableOverExposure < 0) + cam->params.flickerControl.allowableOverExposure = + -find_over_exposure(cam->params.colourParams.brightness); + if(cam->params.flickerControl.flickerMode != 0) + cam->cmd_queue |= COMMAND_SETFLICKERCTRL; + + + /* queue command to update camera */ + cam->cmd_queue |= COMMAND_SETCOLOURPARAMS; + mutex_unlock(&cam->param_lock); + DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n", + vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour, + vp->contrast); + break; + } + + /* get/set capture window */ + case VIDIOCGWIN: + { + struct video_window *vw = arg; + DBG("VIDIOCGWIN\n"); + + *vw = cam->vw; + break; + } + + case VIDIOCSWIN: + { + /* copy_from_user, check validity, copy to internal structure */ + struct video_window *vw = arg; + DBG("VIDIOCSWIN\n"); + + if (vw->clipcount != 0) { /* clipping not supported */ + retval = -EINVAL; + break; + } + if (vw->clips != NULL) { /* clipping not supported */ + retval = -EINVAL; + break; + } + + /* we set the video window to something smaller or equal to what + * is requested by the user??? + */ + mutex_lock(&cam->param_lock); + if (vw->width != cam->vw.width || vw->height != cam->vw.height) { + int video_size = match_videosize(vw->width, vw->height); + + if (video_size < 0) { + retval = -EINVAL; + mutex_unlock(&cam->param_lock); + break; + } + cam->video_size = video_size; + + /* video size is changing, reset the subcapture area */ + memset(&cam->vc, 0, sizeof(cam->vc)); + + set_vw_size(cam); + DBG("%d / %d\n", cam->vw.width, cam->vw.height); + cam->cmd_queue |= COMMAND_SETFORMAT; + } + + mutex_unlock(&cam->param_lock); + + /* setformat ignored by camera during streaming, + * so stop/dispatch/start */ + if (cam->cmd_queue & COMMAND_SETFORMAT) { + DBG("\n"); + dispatch_commands(cam); + } + DBG("%d/%d:%d\n", cam->video_size, + cam->vw.width, cam->vw.height); + break; + } + + /* mmap interface */ + case VIDIOCGMBUF: + { + struct video_mbuf *vm = arg; + int i; + + DBG("VIDIOCGMBUF\n"); + memset(vm, 0, sizeof(*vm)); + vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM; + vm->frames = FRAME_NUM; + for (i = 0; i < FRAME_NUM; i++) + vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i; + break; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap *vm = arg; + int video_size; + + DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame, + vm->width, vm->height); + if (vm->frame<0||vm->frame>=FRAME_NUM) { + retval = -EINVAL; + break; + } + + /* set video format */ + cam->vp.palette = vm->format; + switch(vm->format) { + case VIDEO_PALETTE_GREY: + cam->vp.depth=8; + break; + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_UYVY: + cam->vp.depth = 16; + break; + case VIDEO_PALETTE_RGB24: + cam->vp.depth = 24; + break; + case VIDEO_PALETTE_RGB32: + cam->vp.depth = 32; + break; + default: + retval = -EINVAL; + break; + } + if (retval) + break; + + /* set video size */ + video_size = match_videosize(vm->width, vm->height); + if (video_size < 0) { + retval = -EINVAL; + break; + } + if (video_size != cam->video_size) { + cam->video_size = video_size; + + /* video size is changing, reset the subcapture area */ + memset(&cam->vc, 0, sizeof(cam->vc)); + + set_vw_size(cam); + cam->cmd_queue |= COMMAND_SETFORMAT; + dispatch_commands(cam); + } + /* according to v4l-spec we must start streaming here */ + cam->mmap_kludge = 1; + retval = capture_frame(cam, vm); + + break; + } + + case VIDIOCSYNC: + { + int *frame = arg; + + //DBG("VIDIOCSYNC: %d\n", *frame); + + if (*frame<0 || *frame >= FRAME_NUM) { + retval = -EINVAL; + break; + } + + switch (cam->frame[*frame].state) { + case FRAME_UNUSED: + case FRAME_READY: + case FRAME_GRABBING: + DBG("sync to unused frame %d\n", *frame); + retval = -EINVAL; + break; + + case FRAME_DONE: + cam->frame[*frame].state = FRAME_UNUSED; + //DBG("VIDIOCSYNC: %d synced\n", *frame); + break; + } + if (retval == -EINTR) { + /* FIXME - xawtv does not handle this nice */ + retval = 0; + } + break; + } + + case VIDIOCGCAPTURE: + { + struct video_capture *vc = arg; + + DBG("VIDIOCGCAPTURE\n"); + + *vc = cam->vc; + + break; + } + + case VIDIOCSCAPTURE: + { + struct video_capture *vc = arg; + + DBG("VIDIOCSCAPTURE\n"); + + if (vc->decimation != 0) { /* How should this be used? */ + retval = -EINVAL; + break; + } + if (vc->flags != 0) { /* Even/odd grab not supported */ + retval = -EINVAL; + break; + } + + /* Clip to the resolution we can set for the ROI + (every 8 columns and 4 rows) */ + vc->x = vc->x & ~(__u32)7; + vc->y = vc->y & ~(__u32)3; + vc->width = vc->width & ~(__u32)7; + vc->height = vc->height & ~(__u32)3; + + if(vc->width == 0 || vc->height == 0 || + vc->x + vc->width > cam->vw.width || + vc->y + vc->height > cam->vw.height) { + retval = -EINVAL; + break; + } + + DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height); + + mutex_lock(&cam->param_lock); + + cam->vc.x = vc->x; + cam->vc.y = vc->y; + cam->vc.width = vc->width; + cam->vc.height = vc->height; + + set_vw_size(cam); + cam->cmd_queue |= COMMAND_SETFORMAT; + + mutex_unlock(&cam->param_lock); + + /* setformat ignored by camera during streaming, + * so stop/dispatch/start */ + dispatch_commands(cam); + break; + } + + case VIDIOCGUNIT: + { + struct video_unit *vu = arg; + + DBG("VIDIOCGUNIT\n"); + + vu->video = cam->vdev.minor; + vu->vbi = VIDEO_NO_UNIT; + vu->radio = VIDEO_NO_UNIT; + vu->audio = VIDEO_NO_UNIT; + vu->teletext = VIDEO_NO_UNIT; + + break; + } + + + /* pointless to implement overlay with this camera */ + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCKEY: + /* tuner interface - we have none */ + case VIDIOCGTUNER: + case VIDIOCSTUNER: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + /* audio interface - we have none */ + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + retval = -EINVAL; + break; + default: + retval = -ENOIOCTLCMD; + break; + } + + mutex_unlock(&cam->busy_lock); + return retval; +} + +static long cpia_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(file, cmd, arg, cpia_do_ioctl); +} + + +/* FIXME */ +static int cpia_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = file->private_data; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + unsigned long page, pos; + struct cam_data *cam = video_get_drvdata(dev); + int retval; + + if (!cam || !cam->ops) + return -ENODEV; + + DBG("cpia_mmap: %ld\n", size); + + if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE) + return -EINVAL; + + /* make this _really_ smp-safe */ + if (mutex_lock_interruptible(&cam->busy_lock)) + return -EINTR; + + if (!cam->frame_buf) { /* we do lazy allocation */ + if ((retval = allocate_frame_buf(cam))) { + mutex_unlock(&cam->busy_lock); + return retval; + } + } + + pos = (unsigned long)(cam->frame_buf); + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + mutex_unlock(&cam->busy_lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + DBG("cpia_mmap: %ld\n", size); + mutex_unlock(&cam->busy_lock); + + return 0; +} + +static const struct v4l2_file_operations cpia_fops = { + .owner = THIS_MODULE, + .open = cpia_open, + .release = cpia_close, + .read = cpia_read, + .mmap = cpia_mmap, + .ioctl = cpia_ioctl, +}; + +static struct video_device cpia_template = { + .name = "CPiA Camera", + .fops = &cpia_fops, + .release = video_device_release_empty, +}; + +/* initialise cam_data structure */ +static void reset_camera_struct(struct cam_data *cam) +{ + /* The following parameter values are the defaults from + * "Software Developer's Guide for CPiA Cameras". Any changes + * to the defaults are noted in comments. */ + cam->params.colourParams.brightness = 50; + cam->params.colourParams.contrast = 48; + cam->params.colourParams.saturation = 50; + cam->params.exposure.gainMode = 4; + cam->params.exposure.expMode = 2; /* AEC */ + cam->params.exposure.compMode = 1; + cam->params.exposure.centreWeight = 1; + cam->params.exposure.gain = 0; + cam->params.exposure.fineExp = 0; + cam->params.exposure.coarseExpLo = 185; + cam->params.exposure.coarseExpHi = 0; + cam->params.exposure.redComp = COMP_RED; + cam->params.exposure.green1Comp = COMP_GREEN1; + cam->params.exposure.green2Comp = COMP_GREEN2; + cam->params.exposure.blueComp = COMP_BLUE; + cam->params.colourBalance.balanceMode = 2; /* ACB */ + cam->params.colourBalance.redGain = 32; + cam->params.colourBalance.greenGain = 6; + cam->params.colourBalance.blueGain = 92; + cam->params.apcor.gain1 = 0x18; + cam->params.apcor.gain2 = 0x16; + cam->params.apcor.gain4 = 0x24; + cam->params.apcor.gain8 = 0x34; + cam->params.flickerControl.flickerMode = 0; + cam->params.flickerControl.disabled = 1; + + cam->params.flickerControl.coarseJump = + flicker_jumps[cam->mainsFreq] + [cam->params.sensorFps.baserate] + [cam->params.sensorFps.divisor]; + cam->params.flickerControl.allowableOverExposure = + -find_over_exposure(cam->params.colourParams.brightness); + cam->params.vlOffset.gain1 = 20; + cam->params.vlOffset.gain2 = 24; + cam->params.vlOffset.gain4 = 26; + cam->params.vlOffset.gain8 = 26; + cam->params.compressionParams.hysteresis = 3; + cam->params.compressionParams.threshMax = 11; + cam->params.compressionParams.smallStep = 1; + cam->params.compressionParams.largeStep = 3; + cam->params.compressionParams.decimationHysteresis = 2; + cam->params.compressionParams.frDiffStepThresh = 5; + cam->params.compressionParams.qDiffStepThresh = 3; + cam->params.compressionParams.decimationThreshMod = 2; + /* End of default values from Software Developer's Guide */ + + cam->transfer_rate = 0; + cam->exposure_status = EXPOSURE_NORMAL; + + /* Set Sensor FPS to 15fps. This seems better than 30fps + * for indoor lighting. */ + cam->params.sensorFps.divisor = 1; + cam->params.sensorFps.baserate = 1; + + cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */ + cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */ + + cam->params.format.subSample = SUBSAMPLE_422; + cam->params.format.yuvOrder = YUVORDER_YUYV; + + cam->params.compression.mode = CPIA_COMPRESSION_AUTO; + cam->params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_QUALITY; + cam->params.compressionTarget.targetFR = 15; /* From windows driver */ + cam->params.compressionTarget.targetQ = 5; /* From windows driver */ + + cam->params.qx3.qx3_detected = 0; + cam->params.qx3.toplight = 0; + cam->params.qx3.bottomlight = 0; + cam->params.qx3.button = 0; + cam->params.qx3.cradled = 0; + + cam->video_size = VIDEOSIZE_CIF; + + cam->vp.colour = 32768; /* 50% */ + cam->vp.hue = 32768; /* 50% */ + cam->vp.brightness = 32768; /* 50% */ + cam->vp.contrast = 32768; /* 50% */ + cam->vp.whiteness = 0; /* not used -> grayscale only */ + cam->vp.depth = 24; /* to be set by user */ + cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */ + + cam->vc.x = 0; + cam->vc.y = 0; + cam->vc.width = 0; + cam->vc.height = 0; + + cam->vw.x = 0; + cam->vw.y = 0; + set_vw_size(cam); + cam->vw.chromakey = 0; + cam->vw.flags = 0; + cam->vw.clipcount = 0; + cam->vw.clips = NULL; + + cam->cmd_queue = COMMAND_NONE; + cam->first_frame = 1; + + return; +} + +/* initialize cam_data structure */ +static void init_camera_struct(struct cam_data *cam, + struct cpia_camera_ops *ops ) +{ + int i; + + /* Default everything to 0 */ + memset(cam, 0, sizeof(struct cam_data)); + + cam->ops = ops; + mutex_init(&cam->param_lock); + mutex_init(&cam->busy_lock); + + reset_camera_struct(cam); + + cam->proc_entry = NULL; + + memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template)); + video_set_drvdata(&cam->vdev, cam); + + cam->curframe = 0; + for (i = 0; i < FRAME_NUM; i++) { + cam->frame[i].width = 0; + cam->frame[i].height = 0; + cam->frame[i].state = FRAME_UNUSED; + cam->frame[i].data = NULL; + } + cam->decompressed_frame.width = 0; + cam->decompressed_frame.height = 0; + cam->decompressed_frame.state = FRAME_UNUSED; + cam->decompressed_frame.data = NULL; +} + +struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel) +{ + struct cam_data *camera; + + if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) + return NULL; + + + init_camera_struct( camera, ops ); + camera->lowlevel_data = lowlevel; + + /* register v4l device */ + if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + kfree(camera); + printk(KERN_DEBUG "video_register_device failed\n"); + return NULL; + } + + /* get version information from camera: open/reset/close */ + + /* open cpia */ + if (camera->ops->open(camera->lowlevel_data)) + return camera; + + /* reset the camera */ + if (reset_camera(camera) != 0) { + camera->ops->close(camera->lowlevel_data); + return camera; + } + + /* close cpia */ + camera->ops->close(camera->lowlevel_data); + +#ifdef CONFIG_PROC_FS + create_proc_cpia_cam(camera); +#endif + + printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n", + camera->params.version.firmwareVersion, + camera->params.version.firmwareRevision, + camera->params.version.vcVersion, + camera->params.version.vcRevision); + printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n", + camera->params.pnpID.vendor, + camera->params.pnpID.product, + camera->params.pnpID.deviceRevision); + printk(KERN_INFO " VP-Version: %d.%d %04x\n", + camera->params.vpVersion.vpVersion, + camera->params.vpVersion.vpRevision, + camera->params.vpVersion.cameraHeadID); + + return camera; +} + +void cpia_unregister_camera(struct cam_data *cam) +{ + DBG("unregistering video\n"); + video_unregister_device(&cam->vdev); + if (cam->open_count) { + put_cam(cam->ops); + DBG("camera open -- setting ops to NULL\n"); + cam->ops = NULL; + } + +#ifdef CONFIG_PROC_FS + DBG("destroying /proc/cpia/%s\n", video_device_node_name(&cam->vdev)); + destroy_proc_cpia_cam(cam); +#endif + if (!cam->open_count) { + DBG("freeing camera\n"); + kfree(cam); + } +} + +static int __init cpia_init(void) +{ + printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, + CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); + + printk(KERN_WARNING "Since in-kernel colorspace conversion is not " + "allowed, it is disabled by default now. Users should fix the " + "applications in case they don't work without conversion " + "reenabled by setting the 'colorspace_conv' module " + "parameter to 1\n"); + +#ifdef CONFIG_PROC_FS + proc_cpia_create(); +#endif + + return 0; +} + +static void __exit cpia_exit(void) +{ +#ifdef CONFIG_PROC_FS + proc_cpia_destroy(); +#endif +} + +module_init(cpia_init); +module_exit(cpia_exit); + +/* Exported symbols for modules. */ + +EXPORT_SYMBOL(cpia_register_camera); +EXPORT_SYMBOL(cpia_unregister_camera); diff --git a/drivers/staging/cpia/cpia.h b/drivers/staging/cpia/cpia.h new file mode 100644 index 000000000000..8f0cfee4b8a1 --- /dev/null +++ b/drivers/staging/cpia/cpia.h @@ -0,0 +1,432 @@ +#ifndef cpia_h +#define cpia_h + +/* + * CPiA Parallel Port Video4Linux driver + * + * Supports CPiA based parallel port Video Camera's. + * + * (C) Copyright 1999 Bas Huisman, + * Peter Pregler, + * Scott J. Bertin, + * VLSI Vision Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define CPIA_MAJ_VER 1 +#define CPIA_MIN_VER 2 +#define CPIA_PATCH_VER 3 + +#define CPIA_PP_MAJ_VER CPIA_MAJ_VER +#define CPIA_PP_MIN_VER CPIA_MIN_VER +#define CPIA_PP_PATCH_VER CPIA_PATCH_VER + +#define CPIA_USB_MAJ_VER CPIA_MAJ_VER +#define CPIA_USB_MIN_VER CPIA_MIN_VER +#define CPIA_USB_PATCH_VER CPIA_PATCH_VER + +#define CPIA_MAX_FRAME_SIZE_UNALIGNED (352 * 288 * 4) /* CIF at RGB32 */ +#define CPIA_MAX_FRAME_SIZE ((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */ + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include +#include + +struct cpia_camera_ops +{ + /* open sets privdata to point to structure for this camera. + * Returns negative value on error, otherwise 0. + */ + int (*open)(void *privdata); + + /* Registers callback function cb to be called with cbdata + * when an image is ready. If cb is NULL, only single image grabs + * should be used. cb should immediately call streamRead to read + * the data or data may be lost. Returns negative value on error, + * otherwise 0. + */ + int (*registerCallback)(void *privdata, void (*cb)(void *cbdata), + void *cbdata); + + /* transferCmd sends commands to the camera. command MUST point to + * an 8 byte buffer in kernel space. data can be NULL if no extra + * data is needed. The size of the data is given by the last 2 + * bytes of command. data must also point to memory in kernel space. + * Returns negative value on error, otherwise 0. + */ + int (*transferCmd)(void *privdata, u8 *command, u8 *data); + + /* streamStart initiates stream capture mode. + * Returns negative value on error, otherwise 0. + */ + int (*streamStart)(void *privdata); + + /* streamStop terminates stream capture mode. + * Returns negative value on error, otherwise 0. + */ + int (*streamStop)(void *privdata); + + /* streamRead reads a frame from the camera. buffer points to a + * buffer large enough to hold a complete frame in kernel space. + * noblock indicates if this should be a non blocking read. + * Returns the number of bytes read, or negative value on error. + */ + int (*streamRead)(void *privdata, u8 *buffer, int noblock); + + /* close disables the device until open() is called again. + * Returns negative value on error, otherwise 0. + */ + int (*close)(void *privdata); + + /* If wait_for_stream_ready is non-zero, wait until the streamState + * is STREAM_READY before calling streamRead. + */ + int wait_for_stream_ready; + + /* + * Used to maintain lowlevel module usage counts + */ + struct module *owner; +}; + +struct cpia_frame { + u8 *data; + int count; + int width; + int height; + volatile int state; +}; + +struct cam_params { + struct { + u8 firmwareVersion; + u8 firmwareRevision; + u8 vcVersion; + u8 vcRevision; + } version; + struct { + u16 vendor; + u16 product; + u16 deviceRevision; + } pnpID; + struct { + u8 vpVersion; + u8 vpRevision; + u16 cameraHeadID; + } vpVersion; + struct { + u8 systemState; + u8 grabState; + u8 streamState; + u8 fatalError; + u8 cmdError; + u8 debugFlags; + u8 vpStatus; + u8 errorCode; + } status; + struct { + u8 brightness; + u8 contrast; + u8 saturation; + } colourParams; + struct { + u8 gainMode; + u8 expMode; + u8 compMode; + u8 centreWeight; + u8 gain; + u8 fineExp; + u8 coarseExpLo; + u8 coarseExpHi; + u8 redComp; + u8 green1Comp; + u8 green2Comp; + u8 blueComp; + } exposure; + struct { + u8 balanceMode; + u8 redGain; + u8 greenGain; + u8 blueGain; + } colourBalance; + struct { + u8 divisor; + u8 baserate; + } sensorFps; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } apcor; + struct { + u8 disabled; + u8 flickerMode; + u8 coarseJump; + int allowableOverExposure; + } flickerControl; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } vlOffset; + struct { + u8 mode; + u8 decimation; + } compression; + struct { + u8 frTargeting; + u8 targetFR; + u8 targetQ; + } compressionTarget; + struct { + u8 yThreshold; + u8 uvThreshold; + } yuvThreshold; + struct { + u8 hysteresis; + u8 threshMax; + u8 smallStep; + u8 largeStep; + u8 decimationHysteresis; + u8 frDiffStepThresh; + u8 qDiffStepThresh; + u8 decimationThreshMod; + } compressionParams; + struct { + u8 videoSize; /* CIF/QCIF */ + u8 subSample; + u8 yuvOrder; + } format; + struct { /* Intel QX3 specific data */ + u8 qx3_detected; /* a QX3 is present */ + u8 toplight; /* top light lit , R/W */ + u8 bottomlight; /* bottom light lit, R/W */ + u8 button; /* snapshot button pressed (R/O) */ + u8 cradled; /* microscope is in cradle (R/O) */ + } qx3; + struct { + u8 colStart; /* skip first 8*colStart pixels */ + u8 colEnd; /* finish at 8*colEnd pixels */ + u8 rowStart; /* skip first 4*rowStart lines */ + u8 rowEnd; /* finish at 4*rowEnd lines */ + } roi; + u8 ecpTiming; + u8 streamStartLine; +}; + +enum v4l_camstates { + CPIA_V4L_IDLE = 0, + CPIA_V4L_ERROR, + CPIA_V4L_COMMAND, + CPIA_V4L_GRABBING, + CPIA_V4L_STREAMING, + CPIA_V4L_STREAMING_PAUSED, +}; + +#define FRAME_NUM 2 /* double buffering for now */ + +struct cam_data { + struct list_head cam_data_list; + + struct mutex busy_lock; /* guard against SMP multithreading */ + struct cpia_camera_ops *ops; /* lowlevel driver operations */ + void *lowlevel_data; /* private data for lowlevel driver */ + u8 *raw_image; /* buffer for raw image data */ + struct cpia_frame decompressed_frame; + /* buffer to hold decompressed frame */ + int image_size; /* sizeof last decompressed image */ + int open_count; /* # of process that have camera open */ + + /* camera status */ + int fps; /* actual fps reported by the camera */ + int transfer_rate; /* transfer rate from camera in kB/s */ + u8 mainsFreq; /* for flicker control */ + + /* proc interface */ + struct mutex param_lock; /* params lock for this camera */ + struct cam_params params; /* camera settings */ + struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */ + + /* v4l */ + int video_size; /* VIDEO_SIZE_ */ + volatile enum v4l_camstates camstate; /* v4l layer status */ + struct video_device vdev; /* v4l videodev */ + struct video_picture vp; /* v4l camera settings */ + struct video_window vw; /* v4l capture area */ + struct video_capture vc; /* v4l subcapture area */ + + /* mmap interface */ + int curframe; /* the current frame to grab into */ + u8 *frame_buf; /* frame buffer data */ + struct cpia_frame frame[FRAME_NUM]; + /* FRAME_NUM-buffering, so we need a array */ + + int first_frame; + int mmap_kludge; /* 'wrong' byte order for mmap */ + volatile u32 cmd_queue; /* queued commands */ + int exposure_status; /* EXPOSURE_* */ + int exposure_count; /* number of frames at this status */ +}; + +/* cpia_register_camera is called by low level driver for each camera. + * A unique camera number is returned, or a negative value on error */ +struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel); + +/* cpia_unregister_camera is called by low level driver when a camera + * is removed. This must not fail. */ +void cpia_unregister_camera(struct cam_data *cam); + +/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI + + * one byte 16bit DMA alignment + */ +#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5) + +/* constant value's */ +#define MAGIC_0 0x19 +#define MAGIC_1 0x68 +#define DATA_IN 0xC0 +#define DATA_OUT 0x40 +#define VIDEOSIZE_QCIF 0 /* 176x144 */ +#define VIDEOSIZE_CIF 1 /* 352x288 */ +#define VIDEOSIZE_SIF 2 /* 320x240 */ +#define VIDEOSIZE_QSIF 3 /* 160x120 */ +#define VIDEOSIZE_48_48 4 /* where no one has gone before, iconsize! */ +#define VIDEOSIZE_64_48 5 +#define VIDEOSIZE_128_96 6 +#define VIDEOSIZE_160_120 VIDEOSIZE_QSIF +#define VIDEOSIZE_176_144 VIDEOSIZE_QCIF +#define VIDEOSIZE_192_144 7 +#define VIDEOSIZE_224_168 8 +#define VIDEOSIZE_256_192 9 +#define VIDEOSIZE_288_216 10 +#define VIDEOSIZE_320_240 VIDEOSIZE_SIF +#define VIDEOSIZE_352_288 VIDEOSIZE_CIF +#define VIDEOSIZE_88_72 11 /* quarter CIF */ +#define SUBSAMPLE_420 0 +#define SUBSAMPLE_422 1 +#define YUVORDER_YUYV 0 +#define YUVORDER_UYVY 1 +#define NOT_COMPRESSED 0 +#define COMPRESSED 1 +#define NO_DECIMATION 0 +#define DECIMATION_ENAB 1 +#define EOI 0xff /* End Of Image */ +#define EOL 0xfd /* End Of Line */ +#define FRAME_HEADER_SIZE 64 + +/* Image grab modes */ +#define CPIA_GRAB_SINGLE 0 +#define CPIA_GRAB_CONTINUOUS 1 + +/* Compression parameters */ +#define CPIA_COMPRESSION_NONE 0 +#define CPIA_COMPRESSION_AUTO 1 +#define CPIA_COMPRESSION_MANUAL 2 +#define CPIA_COMPRESSION_TARGET_QUALITY 0 +#define CPIA_COMPRESSION_TARGET_FRAMERATE 1 + +/* Return offsets for GetCameraState */ +#define SYSTEMSTATE 0 +#define GRABSTATE 1 +#define STREAMSTATE 2 +#define FATALERROR 3 +#define CMDERROR 4 +#define DEBUGFLAGS 5 +#define VPSTATUS 6 +#define ERRORCODE 7 + +/* SystemState */ +#define UNINITIALISED_STATE 0 +#define PASS_THROUGH_STATE 1 +#define LO_POWER_STATE 2 +#define HI_POWER_STATE 3 +#define WARM_BOOT_STATE 4 + +/* GrabState */ +#define GRAB_IDLE 0 +#define GRAB_ACTIVE 1 +#define GRAB_DONE 2 + +/* StreamState */ +#define STREAM_NOT_READY 0 +#define STREAM_READY 1 +#define STREAM_OPEN 2 +#define STREAM_PAUSED 3 +#define STREAM_FINISHED 4 + +/* Fatal Error, CmdError, and DebugFlags */ +#define CPIA_FLAG 1 +#define SYSTEM_FLAG 2 +#define INT_CTRL_FLAG 4 +#define PROCESS_FLAG 8 +#define COM_FLAG 16 +#define VP_CTRL_FLAG 32 +#define CAPTURE_FLAG 64 +#define DEBUG_FLAG 128 + +/* VPStatus */ +#define VP_STATE_OK 0x00 + +#define VP_STATE_FAILED_VIDEOINIT 0x01 +#define VP_STATE_FAILED_AECACBINIT 0x02 +#define VP_STATE_AEC_MAX 0x04 +#define VP_STATE_ACB_BMAX 0x08 + +#define VP_STATE_ACB_RMIN 0x10 +#define VP_STATE_ACB_GMIN 0x20 +#define VP_STATE_ACB_RMAX 0x40 +#define VP_STATE_ACB_GMAX 0x80 + +/* default (minimum) compensation values */ +#define COMP_RED 220 +#define COMP_GREEN1 214 +#define COMP_GREEN2 COMP_GREEN1 +#define COMP_BLUE 230 + +/* exposure status */ +#define EXPOSURE_VERY_LIGHT 0 +#define EXPOSURE_LIGHT 1 +#define EXPOSURE_NORMAL 2 +#define EXPOSURE_DARK 3 +#define EXPOSURE_VERY_DARK 4 + +/* ErrorCode */ +#define ERROR_FLICKER_BELOW_MIN_EXP 0x01 /*flicker exposure got below minimum exposure */ +#define ALOG(fmt,args...) printk(fmt, ##args) +#define LOG(fmt,args...) ALOG(KERN_INFO __FILE__ ":%s(%d):" fmt, __func__ , __LINE__ , ##args) + +#ifdef _CPIA_DEBUG_ +#define ADBG(fmt,args...) printk(fmt, jiffies, ##args) +#define DBG(fmt,args...) ADBG(KERN_DEBUG __FILE__" (%ld):%s(%d):" fmt, __func__, __LINE__ , ##args) +#else +#define DBG(fmn,args...) do {} while(0) +#endif + +#define DEB_BYTE(p)\ + DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\ + (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\ + (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0); + +#endif /* __KERNEL__ */ + +#endif /* cpia_h */ diff --git a/drivers/staging/cpia/cpia_pp.c b/drivers/staging/cpia/cpia_pp.c new file mode 100644 index 000000000000..f5604c16a092 --- /dev/null +++ b/drivers/staging/cpia/cpia_pp.c @@ -0,0 +1,869 @@ +/* + * cpia_pp CPiA Parallel Port driver + * + * Supports CPiA based parallel port Video Camera's. + * + * (C) Copyright 1999 Bas Huisman + * (C) Copyright 1999-2000 Scott J. Bertin , + * (C) Copyright 1999-2000 Peter Pregler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ +/* #define _CPIA_DEBUG_ 1 */ + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* #define _CPIA_DEBUG_ define for verbose debug output */ +#include "cpia.h" + +static int cpia_pp_open(void *privdata); +static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata); +static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data); +static int cpia_pp_streamStart(void *privdata); +static int cpia_pp_streamStop(void *privdata); +static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock); +static int cpia_pp_close(void *privdata); + + +#define ABOUT "Parallel port driver for Vision CPiA based cameras" + +#define PACKET_LENGTH 8 + +/* Magic numbers for defining port-device mappings */ +#define PPCPIA_PARPORT_UNSPEC -4 +#define PPCPIA_PARPORT_AUTO -3 +#define PPCPIA_PARPORT_OFF -2 +#define PPCPIA_PARPORT_NONE -1 + +static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; +static char *parport[PARPORT_MAX] = {NULL,}; + +MODULE_AUTHOR("B. Huisman & Peter Pregler "); +MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras"); +MODULE_LICENSE("GPL"); + +module_param_array(parport, charp, NULL, 0); +MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp."); + +struct pp_cam_entry { + struct pardevice *pdev; + struct parport *port; + struct work_struct cb_task; + void (*cb_func)(void *cbdata); + void *cb_data; + int open_count; + wait_queue_head_t wq_stream; + /* image state flags */ + int image_ready; /* we got an interrupt */ + int image_complete; /* we have seen 4 EOI */ + + int streaming; /* we are in streaming mode */ + int stream_irq; +}; + +static struct cpia_camera_ops cpia_pp_ops = +{ + cpia_pp_open, + cpia_pp_registerCallback, + cpia_pp_transferCmd, + cpia_pp_streamStart, + cpia_pp_streamStop, + cpia_pp_streamRead, + cpia_pp_close, + 1, + THIS_MODULE +}; + +static LIST_HEAD(cam_list); +static spinlock_t cam_list_lock_pp; + +/* FIXME */ +static void cpia_parport_enable_irq( struct parport *port ) { + parport_enable_irq(port); + mdelay(10); + return; +} + +static void cpia_parport_disable_irq( struct parport *port ) { + parport_disable_irq(port); + mdelay(10); + return; +} + +/* Special CPiA PPC modes: These are invoked by using the 1284 Extensibility + * Link Flag during negotiation */ +#define UPLOAD_FLAG 0x08 +#define NIBBLE_TRANSFER 0x01 +#define ECP_TRANSFER 0x03 + +#define PARPORT_CHUNK_SIZE PAGE_SIZE + + +static void cpia_pp_run_callback(struct work_struct *work) +{ + void (*cb_func)(void *cbdata); + void *cb_data; + struct pp_cam_entry *cam; + + cam = container_of(work, struct pp_cam_entry, cb_task); + cb_func = cam->cb_func; + cb_data = cam->cb_data; + + cb_func(cb_data); +} + +/**************************************************************************** + * + * CPiA-specific low-level parport functions for nibble uploads + * + ***************************************************************************/ +/* CPiA nonstandard "Nibble" mode (no nDataAvail signal after each byte). */ +/* The standard kernel parport_ieee1284_read_nibble() fails with the CPiA... */ + +static size_t cpia_read_nibble (struct parport *port, + void *buffer, size_t len, + int flags) +{ + /* adapted verbatim, with one change, from + parport_ieee1284_read_nibble() in drivers/parport/ieee1284-ops.c */ + + unsigned char *buf = buffer; + int i; + unsigned char byte = 0; + + len *= 2; /* in nibbles */ + for (i=0; i < len; i++) { + unsigned char nibble; + + /* The CPiA firmware suppresses the use of nDataAvail (nFault LO) + * after every second nibble to signal that more + * data is available. (the total number of Bytes that + * should be sent is known; if too few are received, an error + * will be recorded after a timeout). + * This is incompatible with parport_ieee1284_read_nibble(), + * which expects to find nFault LO after every second nibble. + */ + + /* Solution: modify cpia_read_nibble to only check for + * nDataAvail before the first nibble is sent. + */ + + /* Does the error line indicate end of data? */ + if (((i /*& 1*/) == 0) && + (parport_read_status(port) & PARPORT_STATUS_ERROR)) { + DBG("%s: No more nibble data (%d bytes)\n", + port->name, i/2); + goto end_of_data; + } + + /* Event 7: Set nAutoFd low. */ + parport_frob_control (port, + PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_AUTOFD); + + /* Event 9: nAck goes low. */ + port->ieee1284.phase = IEEE1284_PH_REV_DATA; + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, 0)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 9 (%d bytes)\n", + port->name, i/2); + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + break; + } + + + /* Read a nibble. */ + nibble = parport_read_status (port) >> 3; + nibble &= ~8; + if ((nibble & 0x10) == 0) + nibble |= 8; + nibble &= 0xf; + + /* Event 10: Set nAutoFd high. */ + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + + /* Event 11: nAck goes high. */ + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, + PARPORT_STATUS_ACK)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 11\n", + port->name); + break; + } + + if (i & 1) { + /* Second nibble */ + byte |= nibble << 4; + *buf++ = byte; + } else + byte = nibble; + } + + if (i == len) { + /* Read the last nibble without checking data avail. */ + if (parport_read_status (port) & PARPORT_STATUS_ERROR) { + end_of_data: + /* Go to reverse idle phase. */ + parport_frob_control (port, + PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_AUTOFD); + port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE; + } + else + port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL; + } + + return i/2; +} + +/* CPiA nonstandard "Nibble Stream" mode (2 nibbles per cycle, instead of 1) + * (See CPiA Data sheet p. 31) + * + * "Nibble Stream" mode used by CPiA for uploads to non-ECP ports is a + * nonstandard variant of nibble mode which allows the same (mediocre) + * data flow of 8 bits per cycle as software-enabled ECP by TRISTATE-capable + * parallel ports, but works also for non-TRISTATE-capable ports. + * (Standard nibble mode only send 4 bits per cycle) + * + */ + +static size_t cpia_read_nibble_stream(struct parport *port, + void *buffer, size_t len, + int flags) +{ + int i; + unsigned char *buf = buffer; + int endseen = 0; + + for (i=0; i < len; i++) { + unsigned char nibble[2], byte = 0; + int j; + + /* Image Data is complete when 4 consecutive EOI bytes (0xff) are seen */ + if (endseen > 3 ) + break; + + /* Event 7: Set nAutoFd low. */ + parport_frob_control (port, + PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_AUTOFD); + + /* Event 9: nAck goes low. */ + port->ieee1284.phase = IEEE1284_PH_REV_DATA; + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, 0)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 9 (%d bytes)\n", + port->name, i/2); + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + break; + } + + /* Read lower nibble */ + nibble[0] = parport_read_status (port) >>3; + + /* Event 10: Set nAutoFd high. */ + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + + /* Event 11: nAck goes high. */ + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, + PARPORT_STATUS_ACK)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 11\n", + port->name); + break; + } + + /* Read upper nibble */ + nibble[1] = parport_read_status (port) >>3; + + /* reassemble the byte */ + for (j = 0; j < 2 ; j++ ) { + nibble[j] &= ~8; + if ((nibble[j] & 0x10) == 0) + nibble[j] |= 8; + nibble[j] &= 0xf; + } + byte = (nibble[0] |(nibble[1] << 4)); + *buf++ = byte; + + if(byte == EOI) + endseen++; + else + endseen = 0; + } + return i; +} + +/**************************************************************************** + * + * EndTransferMode + * + ***************************************************************************/ +static void EndTransferMode(struct pp_cam_entry *cam) +{ + parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); +} + +/**************************************************************************** + * + * ForwardSetup + * + ***************************************************************************/ +static int ForwardSetup(struct pp_cam_entry *cam) +{ + int retry; + + /* The CPiA uses ECP protocol for Downloads from the Host to the camera. + * This will be software-emulated if ECP hardware is not present + */ + + /* the usual camera maximum response time is 10ms, but after receiving + * some commands, it needs up to 40ms. (Data Sheet p. 32)*/ + + for(retry = 0; retry < 4; ++retry) { + if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) { + break; + } + mdelay(10); + } + if(retry == 4) { + DBG("Unable to negotiate IEEE1284 ECP Download mode\n"); + return -1; + } + return 0; +} +/**************************************************************************** + * + * ReverseSetup + * + ***************************************************************************/ +static int ReverseSetup(struct pp_cam_entry *cam, int extensibility) +{ + int retry; + int upload_mode, mode = IEEE1284_MODE_ECP; + int transfer_mode = ECP_TRANSFER; + + if (!(cam->port->modes & PARPORT_MODE_ECP) && + !(cam->port->modes & PARPORT_MODE_TRISTATE)) { + mode = IEEE1284_MODE_NIBBLE; + transfer_mode = NIBBLE_TRANSFER; + } + + upload_mode = mode; + if(extensibility) mode = UPLOAD_FLAG|transfer_mode|IEEE1284_EXT_LINK; + + /* the usual camera maximum response time is 10ms, but after + * receiving some commands, it needs up to 40ms. */ + + for(retry = 0; retry < 4; ++retry) { + if(!parport_negotiate(cam->port, mode)) { + break; + } + mdelay(10); + } + if(retry == 4) { + if(extensibility) + DBG("Unable to negotiate upload extensibility mode\n"); + else + DBG("Unable to negotiate upload mode\n"); + return -1; + } + if(extensibility) cam->port->ieee1284.mode = upload_mode; + return 0; +} + +/**************************************************************************** + * + * WritePacket + * + ***************************************************************************/ +static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size) +{ + int retval=0; + int size_written; + + if (packet == NULL) { + return -EINVAL; + } + if (ForwardSetup(cam)) { + DBG("Write failed in setup\n"); + return -EIO; + } + size_written = parport_write(cam->port, packet, size); + if(size_written != size) { + DBG("Write failed, wrote %d/%d\n", size_written, size); + retval = -EIO; + } + EndTransferMode(cam); + return retval; +} + +/**************************************************************************** + * + * ReadPacket + * + ***************************************************************************/ +static int ReadPacket(struct pp_cam_entry *cam, u8 *packet, size_t size) +{ + int retval=0; + + if (packet == NULL) { + return -EINVAL; + } + if (ReverseSetup(cam, 0)) { + return -EIO; + } + + /* support for CPiA variant nibble reads */ + if(cam->port->ieee1284.mode == IEEE1284_MODE_NIBBLE) { + if(cpia_read_nibble(cam->port, packet, size, 0) != size) + retval = -EIO; + } else { + if(parport_read(cam->port, packet, size) != size) + retval = -EIO; + } + EndTransferMode(cam); + return retval; +} + +/**************************************************************************** + * + * cpia_pp_streamStart + * + ***************************************************************************/ +static int cpia_pp_streamStart(void *privdata) +{ + struct pp_cam_entry *cam = privdata; + DBG("\n"); + cam->streaming=1; + cam->image_ready=0; + //if (ReverseSetup(cam,1)) return -EIO; + if(cam->stream_irq) cpia_parport_enable_irq(cam->port); + return 0; +} + +/**************************************************************************** + * + * cpia_pp_streamStop + * + ***************************************************************************/ +static int cpia_pp_streamStop(void *privdata) +{ + struct pp_cam_entry *cam = privdata; + + DBG("\n"); + cam->streaming=0; + cpia_parport_disable_irq(cam->port); + //EndTransferMode(cam); + + return 0; +} + +/**************************************************************************** + * + * cpia_pp_streamRead + * + ***************************************************************************/ +static int cpia_pp_read(struct parport *port, u8 *buffer, int len) +{ + int bytes_read; + + /* support for CPiA variant "nibble stream" reads */ + if(port->ieee1284.mode == IEEE1284_MODE_NIBBLE) + bytes_read = cpia_read_nibble_stream(port,buffer,len,0); + else { + int new_bytes; + for(bytes_read=0; bytes_readstreaming) DBG("%d / %d\n", cam->image_ready, noblock); + if( cam->stream_irq ) { + DBG("%d\n", cam->image_ready); + cam->image_ready--; + } + cam->image_complete=0; + if (0/*cam->streaming*/) { + if(!cam->image_ready) { + if(noblock) return -EWOULDBLOCK; + interruptible_sleep_on(&cam->wq_stream); + if( signal_pending(current) ) return -EINTR; + DBG("%d\n", cam->image_ready); + } + } else { + if (ReverseSetup(cam, 1)) { + DBG("unable to ReverseSetup\n"); + return -EIO; + } + } + endseen = 0; + block_size = PARPORT_CHUNK_SIZE; + while( !cam->image_complete ) { + cond_resched(); + + new_bytes = cpia_pp_read(cam->port, buffer, block_size ); + if( new_bytes <= 0 ) { + break; + } + i=-1; + while(++iimage_complete=1; + break; + } + if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) { + block_size=CPIA_MAX_IMAGE_SIZE-read_bytes; + } + } + EndTransferMode(cam); + return cam->image_complete ? read_bytes : -EIO; +} +/**************************************************************************** + * + * cpia_pp_transferCmd + * + ***************************************************************************/ +static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data) +{ + int err; + int retval=0; + int databytes; + struct pp_cam_entry *cam = privdata; + + if(cam == NULL) { + DBG("Internal driver error: cam is NULL\n"); + return -EINVAL; + } + if(command == NULL) { + DBG("Internal driver error: command is NULL\n"); + return -EINVAL; + } + databytes = (((int)command[7])<<8) | command[6]; + if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) { + DBG("Error writing command\n"); + return err; + } + if(command[0] == DATA_IN) { + u8 buffer[8]; + if(data == NULL) { + DBG("Internal driver error: data is NULL\n"); + return -EINVAL; + } + if((err = ReadPacket(cam, buffer, 8)) < 0) { + DBG("Error reading command result\n"); + return err; + } + memcpy(data, buffer, databytes); + } else if(command[0] == DATA_OUT) { + if(databytes > 0) { + if(data == NULL) { + DBG("Internal driver error: data is NULL\n"); + retval = -EINVAL; + } else { + if((err=WritePacket(cam, data, databytes)) < 0){ + DBG("Error writing command data\n"); + return err; + } + } + } + } else { + DBG("Unexpected first byte of command: %x\n", command[0]); + retval = -EINVAL; + } + return retval; +} + +/**************************************************************************** + * + * cpia_pp_open + * + ***************************************************************************/ +static int cpia_pp_open(void *privdata) +{ + struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata; + + if (cam == NULL) + return -EINVAL; + + if(cam->open_count == 0) { + if (parport_claim(cam->pdev)) { + DBG("failed to claim the port\n"); + return -EBUSY; + } + parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); + parport_data_forward(cam->port); + parport_write_control(cam->port, PARPORT_CONTROL_SELECT); + udelay(50); + parport_write_control(cam->port, + PARPORT_CONTROL_SELECT + | PARPORT_CONTROL_INIT); + } + + ++cam->open_count; + + return 0; +} + +/**************************************************************************** + * + * cpia_pp_registerCallback + * + ***************************************************************************/ +static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), void *cbdata) +{ + struct pp_cam_entry *cam = privdata; + int retval = 0; + + if(cam->port->irq != PARPORT_IRQ_NONE) { + cam->cb_func = cb; + cam->cb_data = cbdata; + INIT_WORK(&cam->cb_task, cpia_pp_run_callback); + } else { + retval = -1; + } + return retval; +} + +/**************************************************************************** + * + * cpia_pp_close + * + ***************************************************************************/ +static int cpia_pp_close(void *privdata) +{ + struct pp_cam_entry *cam = privdata; + if (--cam->open_count == 0) { + parport_release(cam->pdev); + } + return 0; +} + +/**************************************************************************** + * + * cpia_pp_register + * + ***************************************************************************/ +static int cpia_pp_register(struct parport *port) +{ + struct pardevice *pdev = NULL; + struct pp_cam_entry *cam; + struct cam_data *cpia; + + if (!(port->modes & PARPORT_MODE_PCSPP)) { + LOG("port is not supported by CPiA driver\n"); + return -ENXIO; + } + + cam = kzalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); + if (cam == NULL) { + LOG("failed to allocate camera structure\n"); + return -ENOMEM; + } + + pdev = parport_register_device(port, "cpia_pp", NULL, NULL, + NULL, 0, cam); + + if (!pdev) { + LOG("failed to parport_register_device\n"); + kfree(cam); + return -ENXIO; + } + + cam->pdev = pdev; + cam->port = port; + init_waitqueue_head(&cam->wq_stream); + + cam->streaming = 0; + cam->stream_irq = 0; + + if((cpia = cpia_register_camera(&cpia_pp_ops, cam)) == NULL) { + LOG("failed to cpia_register_camera\n"); + parport_unregister_device(pdev); + kfree(cam); + return -ENXIO; + } + spin_lock( &cam_list_lock_pp ); + list_add( &cpia->cam_data_list, &cam_list ); + spin_unlock( &cam_list_lock_pp ); + + return 0; +} + +static void cpia_pp_detach (struct parport *port) +{ + struct list_head *tmp; + struct cam_data *cpia = NULL; + struct pp_cam_entry *cam; + + spin_lock( &cam_list_lock_pp ); + list_for_each (tmp, &cam_list) { + cpia = list_entry(tmp, struct cam_data, cam_data_list); + cam = (struct pp_cam_entry *) cpia->lowlevel_data; + if (cam && cam->port->number == port->number) { + list_del(&cpia->cam_data_list); + break; + } + cpia = NULL; + } + spin_unlock( &cam_list_lock_pp ); + + if (!cpia) { + DBG("cpia_pp_detach failed to find cam_data in cam_list\n"); + return; + } + + cam = (struct pp_cam_entry *) cpia->lowlevel_data; + cpia_unregister_camera(cpia); + if(cam->open_count > 0) + cpia_pp_close(cam); + parport_unregister_device(cam->pdev); + cpia->lowlevel_data = NULL; + kfree(cam); +} + +static void cpia_pp_attach (struct parport *port) +{ + unsigned int i; + + switch (parport_nr[0]) + { + case PPCPIA_PARPORT_UNSPEC: + case PPCPIA_PARPORT_AUTO: + if (port->probe_info[0].class != PARPORT_CLASS_MEDIA || + port->probe_info[0].cmdset == NULL || + strncmp(port->probe_info[0].cmdset, "CPIA_1", 6) != 0) + return; + + cpia_pp_register(port); + + break; + + default: + for (i = 0; i < PARPORT_MAX; ++i) { + if (port->number == parport_nr[i]) { + cpia_pp_register(port); + break; + } + } + break; + } +} + +static struct parport_driver cpia_pp_driver = { + .name = "cpia_pp", + .attach = cpia_pp_attach, + .detach = cpia_pp_detach, +}; + +static int __init cpia_pp_init(void) +{ + printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, + CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER); + + if(parport_nr[0] == PPCPIA_PARPORT_OFF) { + printk(" disabled\n"); + return 0; + } + + spin_lock_init( &cam_list_lock_pp ); + + if (parport_register_driver (&cpia_pp_driver)) { + LOG ("unable to register with parport\n"); + return -EIO; + } + return 0; +} + +static int __init cpia_init(void) +{ + if (parport[0]) { + /* The user gave some parameters. Let's see what they were. */ + if (!strncmp(parport[0], "auto", 4)) { + parport_nr[0] = PPCPIA_PARPORT_AUTO; + } else { + int n; + for (n = 0; n < PARPORT_MAX && parport[n]; n++) { + if (!strncmp(parport[n], "none", 4)) { + parport_nr[n] = PPCPIA_PARPORT_NONE; + } else { + char *ep; + unsigned long r = simple_strtoul(parport[n], &ep, 0); + if (ep != parport[n]) { + parport_nr[n] = r; + } else { + LOG("bad port specifier `%s'\n", parport[n]); + return -ENODEV; + } + } + } + } + } + return cpia_pp_init(); +} + +static void __exit cpia_cleanup(void) +{ + parport_unregister_driver(&cpia_pp_driver); + return; +} + +module_init(cpia_init); +module_exit(cpia_cleanup); diff --git a/drivers/staging/cpia/cpia_usb.c b/drivers/staging/cpia/cpia_usb.c new file mode 100644 index 000000000000..58d193ff591c --- /dev/null +++ b/drivers/staging/cpia/cpia_usb.c @@ -0,0 +1,640 @@ +/* + * cpia_usb CPiA USB driver + * + * Supports CPiA based parallel port Video Camera's. + * + * Copyright (C) 1999 Jochen Scharrlach + * Copyright (C) 1999, 2000 Johannes Erdfelt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ +/* #define _CPIA_DEBUG_ 1 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpia.h" + +#define USB_REQ_CPIA_GRAB_FRAME 0xC1 +#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2 +#define WAIT_FOR_NEXT_FRAME 0 +#define FORCE_FRAME_UPLOAD 1 + +#define FRAMES_PER_DESC 10 +#define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */ +#define CPIA_NUMSBUF 2 +#define STREAM_BUF_SIZE (PAGE_SIZE * 4) +#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) + +struct cpia_sbuf { + char *data; + struct urb *urb; +}; + +#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100) +enum framebuf_status { + FRAME_EMPTY, + FRAME_READING, + FRAME_READY, + FRAME_ERROR, +}; + +struct framebuf { + int length; + enum framebuf_status status; + u8 data[FRAMEBUF_LEN]; + struct framebuf *next; +}; + +struct usb_cpia { + /* Device structure */ + struct usb_device *dev; + + unsigned char iface; + wait_queue_head_t wq_stream; + + int cursbuf; /* Current receiving sbuf */ + struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */ + + int streaming; + int open; + int present; + struct framebuf *buffers[3]; + struct framebuf *curbuff, *workbuff; +}; + +static int cpia_usb_open(void *privdata); +static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata); +static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data); +static int cpia_usb_streamStart(void *privdata); +static int cpia_usb_streamStop(void *privdata); +static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock); +static int cpia_usb_close(void *privdata); + +#define ABOUT "USB driver for Vision CPiA based cameras" + +static struct cpia_camera_ops cpia_usb_ops = { + cpia_usb_open, + cpia_usb_registerCallback, + cpia_usb_transferCmd, + cpia_usb_streamStart, + cpia_usb_streamStop, + cpia_usb_streamRead, + cpia_usb_close, + 0, + THIS_MODULE +}; + +static LIST_HEAD(cam_list); +static spinlock_t cam_list_lock_usb; + +static void cpia_usb_complete(struct urb *urb) +{ + int i; + char *cdata; + struct usb_cpia *ucpia; + + if (!urb || !urb->context) + return; + + ucpia = (struct usb_cpia *) urb->context; + + if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open) + return; + + if (ucpia->workbuff->status == FRAME_EMPTY) { + ucpia->workbuff->status = FRAME_READING; + ucpia->workbuff->length = 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + if (st) + printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st); + + if (FRAMEBUF_LEN < ucpia->workbuff->length + n) { + printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n); + return; + } + + if (n) { + if ((ucpia->workbuff->length > 0) || + (0x19 == cdata[0] && 0x68 == cdata[1])) { + memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n); + ucpia->workbuff->length += n; + } else + DBG("Ignoring packet!\n"); + } else { + if (ucpia->workbuff->length > 4 && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) { + ucpia->workbuff->status = FRAME_READY; + ucpia->curbuff = ucpia->workbuff; + ucpia->workbuff = ucpia->workbuff->next; + ucpia->workbuff->status = FRAME_EMPTY; + ucpia->workbuff->length = 0; + + if (waitqueue_active(&ucpia->wq_stream)) + wake_up_interruptible(&ucpia->wq_stream); + } + } + } + + /* resubmit */ + urb->dev = ucpia->dev; + if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) + printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __func__, i); +} + +static int cpia_usb_open(void *privdata) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + struct urb *urb; + int ret, retval = 0, fx, err; + + if (!ucpia) + return -EINVAL; + + ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ucpia->sbuf[0].data) + return -EINVAL; + + ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ucpia->sbuf[1].data) { + retval = -EINVAL; + goto error_0; + } + + ret = usb_set_interface(ucpia->dev, ucpia->iface, 3); + if (ret < 0) { + printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret); + retval = -EBUSY; + goto error_1; + } + + ucpia->buffers[0]->status = FRAME_EMPTY; + ucpia->buffers[0]->length = 0; + ucpia->buffers[1]->status = FRAME_EMPTY; + ucpia->buffers[1]->length = 0; + ucpia->buffers[2]->status = FRAME_EMPTY; + ucpia->buffers[2]->length = 0; + ucpia->curbuff = ucpia->buffers[0]; + ucpia->workbuff = ucpia->buffers[1]; + + /* We double buffer the Iso lists, and also know the polling + * interval is every frame (1 == (1 << (bInterval -1))). + */ + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if (!urb) { + printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); + retval = -ENOMEM; + goto error_1; + } + + ucpia->sbuf[0].urb = urb; + urb->dev = ucpia->dev; + urb->context = ucpia; + urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = ucpia->sbuf[0].data; + urb->complete = cpia_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->interval = 1; + urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if (!urb) { + printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 1\n"); + retval = -ENOMEM; + goto error_urb0; + } + + ucpia->sbuf[1].urb = urb; + urb->dev = ucpia->dev; + urb->context = ucpia; + urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = ucpia->sbuf[1].data; + urb->complete = cpia_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->interval = 1; + urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + + /* queue the ISO urbs, and resubmit in the completion handler */ + err = usb_submit_urb(ucpia->sbuf[0].urb, GFP_KERNEL); + if (err) { + printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n", + err); + goto error_urb1; + } + err = usb_submit_urb(ucpia->sbuf[1].urb, GFP_KERNEL); + if (err) { + printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n", + err); + goto error_urb1; + } + + ucpia->streaming = 1; + ucpia->open = 1; + + return 0; + +error_urb1: /* free urb 1 */ + usb_free_urb(ucpia->sbuf[1].urb); + ucpia->sbuf[1].urb = NULL; +error_urb0: /* free urb 0 */ + usb_free_urb(ucpia->sbuf[0].urb); + ucpia->sbuf[0].urb = NULL; +error_1: + kfree (ucpia->sbuf[1].data); + ucpia->sbuf[1].data = NULL; +error_0: + kfree (ucpia->sbuf[0].data); + ucpia->sbuf[0].data = NULL; + + return retval; +} + +// +// convenience functions +// + +/**************************************************************************** + * + * WritePacket + * + ***************************************************************************/ +static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size) +{ + if (!packet) + return -EINVAL; + + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + packet[1] + (packet[0] << 8), + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + packet[2] + (packet[3] << 8), + packet[4] + (packet[5] << 8), buf, size, 1000); +} + +/**************************************************************************** + * + * ReadPacket + * + ***************************************************************************/ +static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size) +{ + if (!packet || size <= 0) + return -EINVAL; + + return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + packet[1] + (packet[0] << 8), + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + packet[2] + (packet[3] << 8), + packet[4] + (packet[5] << 8), buf, size, 1000); +} + +static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data) +{ + int err = 0; + int databytes; + struct usb_cpia *ucpia = (struct usb_cpia *)privdata; + struct usb_device *udev = ucpia->dev; + + if (!udev) { + DBG("Internal driver error: udev is NULL\n"); + return -EINVAL; + } + + if (!command) { + DBG("Internal driver error: command is NULL\n"); + return -EINVAL; + } + + databytes = (((int)command[7])<<8) | command[6]; + + if (command[0] == DATA_IN) { + u8 buffer[8]; + + if (!data) { + DBG("Internal driver error: data is NULL\n"); + return -EINVAL; + } + + err = ReadPacket(udev, command, buffer, 8); + if (err < 0) + return err; + + memcpy(data, buffer, databytes); + } else if(command[0] == DATA_OUT) + WritePacket(udev, command, data, databytes); + else { + DBG("Unexpected first byte of command: %x\n", command[0]); + err = -EINVAL; + } + + return 0; +} + +static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamStart(void *privdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamStop(void *privdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + struct framebuf *mybuff; + + if (!ucpia || !ucpia->present) + return -1; + + if (ucpia->curbuff->status != FRAME_READY) + interruptible_sleep_on(&ucpia->wq_stream); + else + DBG("Frame already waiting!\n"); + + mybuff = ucpia->curbuff; + + if (!mybuff) + return -1; + + if (mybuff->status != FRAME_READY || mybuff->length < 4) { + DBG("Something went wrong!\n"); + return -1; + } + + memcpy(frame, mybuff->data, mybuff->length); + mybuff->status = FRAME_EMPTY; + +/* DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n", */ +/* mybuff->length, frame[0], frame[1], */ +/* frame[mybuff->length-4], frame[mybuff->length-3], */ +/* frame[mybuff->length-2], frame[mybuff->length-1]); */ + + return mybuff->length; +} + +static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try) +{ + if (!ucpia->streaming) + return; + + ucpia->streaming = 0; + + /* Set packet size to 0 */ + if (try) { + int ret; + + ret = usb_set_interface(ucpia->dev, ucpia->iface, 0); + if (ret < 0) { + printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret); + return; + } + } + + /* Unschedule all of the iso td's */ + if (ucpia->sbuf[1].urb) { + usb_kill_urb(ucpia->sbuf[1].urb); + usb_free_urb(ucpia->sbuf[1].urb); + ucpia->sbuf[1].urb = NULL; + } + + kfree(ucpia->sbuf[1].data); + ucpia->sbuf[1].data = NULL; + + if (ucpia->sbuf[0].urb) { + usb_kill_urb(ucpia->sbuf[0].urb); + usb_free_urb(ucpia->sbuf[0].urb); + ucpia->sbuf[0].urb = NULL; + } + + kfree(ucpia->sbuf[0].data); + ucpia->sbuf[0].data = NULL; +} + +static int cpia_usb_close(void *privdata) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + + if(!ucpia) + return -ENODEV; + + ucpia->open = 0; + + /* ucpia->present = 0 protects against trying to reset the + * alt setting if camera is physically disconnected while open */ + cpia_usb_free_resources(ucpia, ucpia->present); + + return 0; +} + +/* Probing and initializing */ + +static int cpia_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_interface *interface; + struct usb_cpia *ucpia; + struct cam_data *cam; + int ret; + + /* A multi-config CPiA camera? */ + if (udev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + interface = intf->cur_altsetting; + + printk(KERN_INFO "USB CPiA camera found\n"); + + ucpia = kzalloc(sizeof(*ucpia), GFP_KERNEL); + if (!ucpia) { + printk(KERN_ERR "couldn't kmalloc cpia struct\n"); + return -ENOMEM; + } + + ucpia->dev = udev; + ucpia->iface = interface->desc.bInterfaceNumber; + init_waitqueue_head(&ucpia->wq_stream); + + ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0])); + if (!ucpia->buffers[0]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 0\n"); + goto fail_alloc_0; + } + + ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1])); + if (!ucpia->buffers[1]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 1\n"); + goto fail_alloc_1; + } + + ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2])); + if (!ucpia->buffers[2]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 2\n"); + goto fail_alloc_2; + } + + ucpia->buffers[0]->next = ucpia->buffers[1]; + ucpia->buffers[1]->next = ucpia->buffers[2]; + ucpia->buffers[2]->next = ucpia->buffers[0]; + + ret = usb_set_interface(udev, ucpia->iface, 0); + if (ret < 0) { + printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret); + /* goto fail_all; */ + } + + /* Before register_camera, important */ + ucpia->present = 1; + + cam = cpia_register_camera(&cpia_usb_ops, ucpia); + if (!cam) { + LOG("failed to cpia_register_camera\n"); + goto fail_all; + } + + spin_lock( &cam_list_lock_usb ); + list_add( &cam->cam_data_list, &cam_list ); + spin_unlock( &cam_list_lock_usb ); + + usb_set_intfdata(intf, cam); + return 0; + +fail_all: + vfree(ucpia->buffers[2]); + ucpia->buffers[2] = NULL; +fail_alloc_2: + vfree(ucpia->buffers[1]); + ucpia->buffers[1] = NULL; +fail_alloc_1: + vfree(ucpia->buffers[0]); + ucpia->buffers[0] = NULL; +fail_alloc_0: + kfree(ucpia); + return -EIO; +} + +static void cpia_disconnect(struct usb_interface *intf); + +static struct usb_device_id cpia_id_table [] = { + { USB_DEVICE(0x0553, 0x0002) }, + { USB_DEVICE(0x0813, 0x0001) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, cpia_id_table); +MODULE_LICENSE("GPL"); + + +static struct usb_driver cpia_driver = { + .name = "cpia", + .probe = cpia_probe, + .disconnect = cpia_disconnect, + .id_table = cpia_id_table, +}; + +static void cpia_disconnect(struct usb_interface *intf) +{ + struct cam_data *cam = usb_get_intfdata(intf); + struct usb_cpia *ucpia; + + usb_set_intfdata(intf, NULL); + if (!cam) + return; + + ucpia = (struct usb_cpia *) cam->lowlevel_data; + spin_lock( &cam_list_lock_usb ); + list_del(&cam->cam_data_list); + spin_unlock( &cam_list_lock_usb ); + + ucpia->present = 0; + + cpia_unregister_camera(cam); + if(ucpia->open) + cpia_usb_close(cam->lowlevel_data); + + ucpia->curbuff->status = FRAME_ERROR; + + if (waitqueue_active(&ucpia->wq_stream)) + wake_up_interruptible(&ucpia->wq_stream); + + ucpia->curbuff = ucpia->workbuff = NULL; + + vfree(ucpia->buffers[2]); + ucpia->buffers[2] = NULL; + + vfree(ucpia->buffers[1]); + ucpia->buffers[1] = NULL; + + vfree(ucpia->buffers[0]); + ucpia->buffers[0] = NULL; + + cam->lowlevel_data = NULL; + kfree(ucpia); +} + +static int __init usb_cpia_init(void) +{ + printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, + CPIA_USB_MAJ_VER,CPIA_USB_MIN_VER,CPIA_USB_PATCH_VER); + + spin_lock_init(&cam_list_lock_usb); + return usb_register(&cpia_driver); +} + +static void __exit usb_cpia_cleanup(void) +{ + usb_deregister(&cpia_driver); +} + + +module_init (usb_cpia_init); +module_exit (usb_cpia_cleanup); + -- cgit v1.2.3-70-g09d2 From 96322b80e29802d2d3087987f6dc4e5aa19df64b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 30 Sep 2010 08:26:28 -0300 Subject: V4L/DVB: Deprecate stradis driver The driver author seems to not worked on this driver since its conversion from 2.2 to 2.4. Nobody is known to have a stradis hardware for testing. As it still uses V4L1 API, BKL and probably some other old stuff, someone would need to work on it to preserve the driver. Instead of investing time and efforts to keep porting it to work with new API's, it seems better to just drop the driver. So, let's move it to drivers/staging and label it to die at 2.6.38, if nobody cares enough to port parallel port support to gspca or to create a new driver that uses the same gspca-cpia sub-driver. Signed-off-by: Mauro Carvalho Chehab --- Documentation/feature-removal-schedule.txt | 4 +- drivers/media/video/Kconfig | 8 - drivers/media/video/Makefile | 1 - drivers/media/video/stradis.c | 2213 ---------------------------- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/stradis/Kconfig | 7 + drivers/staging/stradis/Makefile | 3 + drivers/staging/stradis/TODO | 6 + drivers/staging/stradis/stradis.c | 2213 ++++++++++++++++++++++++++++ 10 files changed, 2234 insertions(+), 2224 deletions(-) delete mode 100644 drivers/media/video/stradis.c create mode 100644 drivers/staging/stradis/Kconfig create mode 100644 drivers/staging/stradis/Makefile create mode 100644 drivers/staging/stradis/TODO create mode 100644 drivers/staging/stradis/stradis.c (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 448722555648..d87ee1760e3a 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -118,8 +118,8 @@ Who: Mauro Carvalho Chehab What: Video4Linux obsolete drivers using V4L1 API When: kernel 2.6.38 -Files: drivers/staging/cpia/* -Check: drivers/staging/cpia/cpia.c +Files: drivers/staging/cpia/* drivers/staging/stradis/* +Check: drivers/staging/cpia/cpia.c drivers/staging/stradis/stradis.c Why: There are some drivers still using V4L1 API, despite all efforts we've done to migrate. Those drivers are for obsolete hardware that the old maintainer didn't care (or not have the hardware anymore), and that no other developer diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 4f21ef877f37..75404fc8e5c2 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -609,14 +609,6 @@ config VIDEO_VINO Say Y here to build in support for the Vino video input system found on SGI Indy machines. -config VIDEO_STRADIS - tristate "Stradis 4:2:2 MPEG-2 video driver (EXPERIMENTAL)" - depends on EXPERIMENTAL && PCI && VIDEO_V4L1 && VIRT_TO_BUS - help - Say Y here to enable support for the Stradis 4:2:2 MPEG-2 video - driver for PCI. There is a product page at - . - source "drivers/media/video/zoran/Kconfig" config VIDEO_MEYE diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index b947160a8cb0..118196c06fcd 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -91,7 +91,6 @@ obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o obj-$(CONFIG_VIDEO_W9966) += w9966.o obj-$(CONFIG_VIDEO_PMS) += pms.o obj-$(CONFIG_VIDEO_VINO) += vino.o -obj-$(CONFIG_VIDEO_STRADIS) += stradis.o obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ diff --git a/drivers/media/video/stradis.c b/drivers/media/video/stradis.c deleted file mode 100644 index a057824e7ebc..000000000000 --- a/drivers/media/video/stradis.c +++ /dev/null @@ -1,2213 +0,0 @@ -/* - * stradis.c - stradis 4:2:2 mpeg decoder driver - * - * Stradis 4:2:2 MPEG-2 Decoder Driver - * Copyright (C) 1999 Nathan Laredo - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "saa7146.h" -#include "saa7146reg.h" -#include "ibmmpeg2.h" -#include "saa7121.h" -#include "cs8420.h" - -#define DEBUG(x) /* debug driver */ -#undef IDEBUG /* debug irq handler */ -#undef MDEBUG /* debug memory management */ - -#define SAA7146_MAX 6 - -static struct saa7146 saa7146s[SAA7146_MAX]; - -static int saa_num; /* number of SAA7146s in use */ - -static int video_nr = -1; -module_param(video_nr, int, 0); -MODULE_LICENSE("GPL"); - -#define nDebNormal 0x00480000 -#define nDebNoInc 0x00480000 -#define nDebVideo 0xd0480000 -#define nDebAudio 0xd0400000 -#define nDebDMA 0x02c80000 - -#define oDebNormal 0x13c80000 -#define oDebNoInc 0x13c80000 -#define oDebVideo 0xd1080000 -#define oDebAudio 0xd1080000 -#define oDebDMA 0x03080000 - -#define NewCard (saa->boardcfg[3]) -#define ChipControl (saa->boardcfg[1]) -#define NTSCFirstActive (saa->boardcfg[4]) -#define PALFirstActive (saa->boardcfg[5]) -#define NTSCLastActive (saa->boardcfg[54]) -#define PALLastActive (saa->boardcfg[55]) -#define Have2MB (saa->boardcfg[18] & 0x40) -#define HaveCS8420 (saa->boardcfg[18] & 0x04) -#define IBMMPEGCD20 (saa->boardcfg[18] & 0x20) -#define HaveCS3310 (saa->boardcfg[18] & 0x01) -#define CS3310MaxLvl ((saa->boardcfg[30] << 8) | saa->boardcfg[31]) -#define HaveCS4341 (saa->boardcfg[40] == 2) -#define SDIType (saa->boardcfg[27]) -#define CurrentMode (saa->boardcfg[2]) - -#define debNormal (NewCard ? nDebNormal : oDebNormal) -#define debNoInc (NewCard ? nDebNoInc : oDebNoInc) -#define debVideo (NewCard ? nDebVideo : oDebVideo) -#define debAudio (NewCard ? nDebAudio : oDebAudio) -#define debDMA (NewCard ? nDebDMA : oDebDMA) - -#ifdef USE_RESCUE_EEPROM_SDM275 -static unsigned char rescue_eeprom[64] = { - 0x00, 0x01, 0x04, 0x13, 0x26, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x43, 0x63, - 0x22, 0x01, 0x29, 0x15, 0x73, 0x00, 0x1f, 'd', 'e', 'c', 'x', 'l', - 'd', 'v', 'a', 0x02, 0x00, 0x01, 0x00, 0xcc, 0xa4, 0x63, 0x09, 0xe2, - 0x10, 0x00, 0x0a, 0x00, 0x02, 0x02, 'd', 'e', 'c', 'x', 'l', 'a', - 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, -}; -#endif - -/* ----------------------------------------------------------------------- */ -/* Hardware I2C functions */ -static void I2CWipe(struct saa7146 *saa) -{ - int i; - /* set i2c to ~=100kHz, abort transfer, clear busy */ - saawrite(0x600 | SAA7146_I2C_ABORT, SAA7146_I2C_STATUS); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); - saawrite(0x600, SAA7146_I2C_STATUS); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); - saawrite(0x600, SAA7146_I2C_STATUS); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); -} - -/* read I2C */ -static int I2CRead(struct saa7146 *saa, unsigned char addr, - unsigned char subaddr, int dosub) -{ - int i; - - if (saaread(SAA7146_I2C_STATUS) & 0x3c) - I2CWipe(saa); - for (i = 0; - i < 1000 && (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); - i++) - schedule(); - if (i == 1000) - I2CWipe(saa); - if (dosub) - saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 8) | - ((subaddr & 0xff) << 16) | 0xed, SAA7146_I2C_TRANSFER); - else - saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 16) | - 0xf1, SAA7146_I2C_TRANSFER); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); - /* wait for valid data */ - for (i = 0; i < 1000 && - (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++) - schedule(); - if (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_ERR) - return -1; - if (i == 1000) - printk("i2c setup read timeout\n"); - saawrite(0x41, SAA7146_I2C_TRANSFER); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); - /* wait for valid data */ - for (i = 0; i < 1000 && - (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_BUSY); i++) - schedule(); - if (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_ERR) - return -1; - if (i == 1000) - printk("i2c read timeout\n"); - return ((saaread(SAA7146_I2C_TRANSFER) >> 24) & 0xff); -} - -/* set both to write both bytes, reset it to write only b1 */ - -static int I2CWrite(struct saa7146 *saa, unsigned char addr, unsigned char b1, - unsigned char b2, int both) -{ - int i; - u32 data; - - if (saaread(SAA7146_I2C_STATUS) & 0x3c) - I2CWipe(saa); - for (i = 0; i < 1000 && - (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++) - schedule(); - if (i == 1000) - I2CWipe(saa); - data = ((addr & 0xfe) << 24) | ((b1 & 0xff) << 16); - if (both) - data |= ((b2 & 0xff) << 8) | 0xe5; - else - data |= 0xd1; - saawrite(data, SAA7146_I2C_TRANSFER); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | SAA7146_MC2_UPLD_I2C, - SAA7146_MC2); - return 0; -} - -static void attach_inform(struct saa7146 *saa, int id) -{ - int i; - - DEBUG(printk(KERN_DEBUG "stradis%d: i2c: device found=%02x\n", saa->nr, - id)); - if (id == 0xa0) { /* we have rev2 or later board, fill in info */ - for (i = 0; i < 64; i++) - saa->boardcfg[i] = I2CRead(saa, 0xa0, i, 1); -#ifdef USE_RESCUE_EEPROM_SDM275 - if (saa->boardcfg[0] != 0) { - printk("stradis%d: WARNING: EEPROM STORED VALUES HAVE " - "BEEN IGNORED\n", saa->nr); - for (i = 0; i < 64; i++) - saa->boardcfg[i] = rescue_eeprom[i]; - } -#endif - printk("stradis%d: config =", saa->nr); - for (i = 0; i < 51; i++) { - printk(" %02x", saa->boardcfg[i]); - } - printk("\n"); - } -} - -static void I2CBusScan(struct saa7146 *saa) -{ - int i; - for (i = 0; i < 0xff; i += 2) - if ((I2CRead(saa, i, 0, 0)) >= 0) - attach_inform(saa, i); -} - -static int debiwait_maxwait; - -static int wait_for_debi_done(struct saa7146 *saa) -{ - int i; - - /* wait for registers to be programmed */ - for (i = 0; i < 100000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_DEBI); i++) - saaread(SAA7146_MC2); - /* wait for transfer to complete */ - for (i = 0; i < 500000 && - (saaread(SAA7146_PSR) & SAA7146_PSR_DEBI_S); i++) - saaread(SAA7146_MC2); - - if (i > debiwait_maxwait) - printk("wait-for-debi-done maxwait: %d\n", - debiwait_maxwait = i); - - if (i == 500000) - return -1; - - return 0; -} - -static int debiwrite(struct saa7146 *saa, u32 config, int addr, - u32 val, int count) -{ - u32 cmd; - if (count <= 0 || count > 32764) - return -1; - if (wait_for_debi_done(saa) < 0) - return -1; - saawrite(config, SAA7146_DEBI_CONFIG); - if (count <= 4) /* immediate transfer */ - saawrite(val, SAA7146_DEBI_AD); - else /* block transfer */ - saawrite(virt_to_bus(saa->dmadebi), SAA7146_DEBI_AD); - saawrite((cmd = (count << 17) | (addr & 0xffff)), SAA7146_DEBI_COMMAND); - saawrite((SAA7146_MC2_UPLD_DEBI << 16) | SAA7146_MC2_UPLD_DEBI, - SAA7146_MC2); - return 0; -} - -static u32 debiread(struct saa7146 *saa, u32 config, int addr, int count) -{ - u32 result = 0; - - if (count > 32764 || count <= 0) - return 0; - if (wait_for_debi_done(saa) < 0) - return 0; - saawrite(virt_to_bus(saa->dmadebi), SAA7146_DEBI_AD); - saawrite((count << 17) | 0x10000 | (addr & 0xffff), - SAA7146_DEBI_COMMAND); - saawrite(config, SAA7146_DEBI_CONFIG); - saawrite((SAA7146_MC2_UPLD_DEBI << 16) | SAA7146_MC2_UPLD_DEBI, - SAA7146_MC2); - if (count > 4) /* not an immediate transfer */ - return count; - wait_for_debi_done(saa); - result = saaread(SAA7146_DEBI_AD); - if (count == 1) - result &= 0xff; - if (count == 2) - result &= 0xffff; - if (count == 3) - result &= 0xffffff; - return result; -} - -static void do_irq_send_data(struct saa7146 *saa) -{ - int split, audbytes, vidbytes; - - saawrite(SAA7146_PSR_PIN1, SAA7146_IER); - /* if special feature mode in effect, disable audio sending */ - if (saa->playmode != VID_PLAY_NORMAL) - saa->audtail = saa->audhead = 0; - if (saa->audhead <= saa->audtail) - audbytes = saa->audtail - saa->audhead; - else - audbytes = 65536 - (saa->audhead - saa->audtail); - if (saa->vidhead <= saa->vidtail) - vidbytes = saa->vidtail - saa->vidhead; - else - vidbytes = 524288 - (saa->vidhead - saa->vidtail); - if (audbytes == 0 && vidbytes == 0 && saa->osdtail == saa->osdhead) { - saawrite(0, SAA7146_IER); - return; - } - /* if at least 1 block audio waiting and audio fifo isn't full */ - if (audbytes >= 2048 && (debiread(saa, debNormal, IBM_MP2_AUD_FIFO, 2) - & 0xff) < 60) { - if (saa->audhead > saa->audtail) - split = 65536 - saa->audhead; - else - split = 0; - audbytes = 2048; - if (split > 0 && split < 2048) { - memcpy(saa->dmadebi, saa->audbuf + saa->audhead, split); - saa->audhead = 0; - audbytes -= split; - } else - split = 0; - memcpy(saa->dmadebi + split, saa->audbuf + saa->audhead, - audbytes); - saa->audhead += audbytes; - saa->audhead &= 0xffff; - debiwrite(saa, debAudio, (NewCard ? IBM_MP2_AUD_FIFO : - IBM_MP2_AUD_FIFOW), 0, 2048); - wake_up_interruptible(&saa->audq); - /* if at least 1 block video waiting and video fifo isn't full */ - } else if (vidbytes >= 30720 && (debiread(saa, debNormal, - IBM_MP2_FIFO, 2)) < 16384) { - if (saa->vidhead > saa->vidtail) - split = 524288 - saa->vidhead; - else - split = 0; - vidbytes = 30720; - if (split > 0 && split < 30720) { - memcpy(saa->dmadebi, saa->vidbuf + saa->vidhead, split); - saa->vidhead = 0; - vidbytes -= split; - } else - split = 0; - memcpy(saa->dmadebi + split, saa->vidbuf + saa->vidhead, - vidbytes); - saa->vidhead += vidbytes; - saa->vidhead &= 0x7ffff; - debiwrite(saa, debVideo, (NewCard ? IBM_MP2_FIFO : - IBM_MP2_FIFOW), 0, 30720); - wake_up_interruptible(&saa->vidq); - } - saawrite(SAA7146_PSR_DEBI_S | SAA7146_PSR_PIN1, SAA7146_IER); -} - -static void send_osd_data(struct saa7146 *saa) -{ - int size = saa->osdtail - saa->osdhead; - if (size > 30720) - size = 30720; - /* ensure some multiple of 8 bytes is transferred */ - size = 8 * ((size + 8) >> 3); - if (size) { - debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR, - (saa->osdhead >> 3), 2); - memcpy(saa->dmadebi, &saa->osdbuf[saa->osdhead], size); - saa->osdhead += size; - /* block transfer of next 8 bytes to ~32k bytes */ - debiwrite(saa, debNormal, IBM_MP2_OSD_DATA, 0, size); - } - if (saa->osdhead >= saa->osdtail) { - saa->osdhead = saa->osdtail = 0; - debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); - } -} - -static irqreturn_t saa7146_irq(int irq, void *dev_id) -{ - struct saa7146 *saa = dev_id; - u32 stat, astat; - int count; - int handled = 0; - - count = 0; - while (1) { - /* get/clear interrupt status bits */ - stat = saaread(SAA7146_ISR); - astat = stat & saaread(SAA7146_IER); - if (!astat) - break; - handled = 1; - saawrite(astat, SAA7146_ISR); - if (astat & SAA7146_PSR_DEBI_S) { - do_irq_send_data(saa); - } - if (astat & SAA7146_PSR_PIN1) { - int istat; - /* the following read will trigger DEBI_S */ - istat = debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); - if (istat & 1) { - saawrite(0, SAA7146_IER); - send_osd_data(saa); - saawrite(SAA7146_PSR_DEBI_S | - SAA7146_PSR_PIN1, SAA7146_IER); - } - if (istat & 0x20) { /* Video Start */ - saa->vidinfo.frame_count++; - } - if (istat & 0x400) { /* Picture Start */ - /* update temporal reference */ - } - if (istat & 0x200) { /* Picture Resolution Change */ - /* read new resolution */ - } - if (istat & 0x100) { /* New User Data found */ - /* read new user data */ - } - if (istat & 0x1000) { /* new GOP/SMPTE */ - /* read new SMPTE */ - } - if (istat & 0x8000) { /* Sequence Start Code */ - /* reset frame counter, load sizes */ - saa->vidinfo.frame_count = 0; - saa->vidinfo.h_size = 704; - saa->vidinfo.v_size = 480; -#if 0 - if (saa->endmarkhead != saa->endmarktail) { - saa->audhead = - saa->endmark[saa->endmarkhead]; - saa->endmarkhead++; - if (saa->endmarkhead >= MAX_MARKS) - saa->endmarkhead = 0; - } -#endif - } - if (istat & 0x4000) { /* Sequence Error Code */ - if (saa->endmarkhead != saa->endmarktail) { - saa->audhead = - saa->endmark[saa->endmarkhead]; - saa->endmarkhead++; - if (saa->endmarkhead >= MAX_MARKS) - saa->endmarkhead = 0; - } - } - } -#ifdef IDEBUG - if (astat & SAA7146_PSR_PPEF) { - IDEBUG(printk("stradis%d irq: PPEF\n", saa->nr)); - } - if (astat & SAA7146_PSR_PABO) { - IDEBUG(printk("stradis%d irq: PABO\n", saa->nr)); - } - if (astat & SAA7146_PSR_PPED) { - IDEBUG(printk("stradis%d irq: PPED\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_I1) { - IDEBUG(printk("stradis%d irq: RPS_I1\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_I0) { - IDEBUG(printk("stradis%d irq: RPS_I0\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_LATE1) { - IDEBUG(printk("stradis%d irq: RPS_LATE1\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_LATE0) { - IDEBUG(printk("stradis%d irq: RPS_LATE0\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_E1) { - IDEBUG(printk("stradis%d irq: RPS_E1\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_E0) { - IDEBUG(printk("stradis%d irq: RPS_E0\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_TO1) { - IDEBUG(printk("stradis%d irq: RPS_TO1\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_TO0) { - IDEBUG(printk("stradis%d irq: RPS_TO0\n", saa->nr)); - } - if (astat & SAA7146_PSR_UPLD) { - IDEBUG(printk("stradis%d irq: UPLD\n", saa->nr)); - } - if (astat & SAA7146_PSR_DEBI_E) { - IDEBUG(printk("stradis%d irq: DEBI_E\n", saa->nr)); - } - if (astat & SAA7146_PSR_I2C_S) { - IDEBUG(printk("stradis%d irq: I2C_S\n", saa->nr)); - } - if (astat & SAA7146_PSR_I2C_E) { - IDEBUG(printk("stradis%d irq: I2C_E\n", saa->nr)); - } - if (astat & SAA7146_PSR_A2_IN) { - IDEBUG(printk("stradis%d irq: A2_IN\n", saa->nr)); - } - if (astat & SAA7146_PSR_A2_OUT) { - IDEBUG(printk("stradis%d irq: A2_OUT\n", saa->nr)); - } - if (astat & SAA7146_PSR_A1_IN) { - IDEBUG(printk("stradis%d irq: A1_IN\n", saa->nr)); - } - if (astat & SAA7146_PSR_A1_OUT) { - IDEBUG(printk("stradis%d irq: A1_OUT\n", saa->nr)); - } - if (astat & SAA7146_PSR_AFOU) { - IDEBUG(printk("stradis%d irq: AFOU\n", saa->nr)); - } - if (astat & SAA7146_PSR_V_PE) { - IDEBUG(printk("stradis%d irq: V_PE\n", saa->nr)); - } - if (astat & SAA7146_PSR_VFOU) { - IDEBUG(printk("stradis%d irq: VFOU\n", saa->nr)); - } - if (astat & SAA7146_PSR_FIDA) { - IDEBUG(printk("stradis%d irq: FIDA\n", saa->nr)); - } - if (astat & SAA7146_PSR_FIDB) { - IDEBUG(printk("stradis%d irq: FIDB\n", saa->nr)); - } - if (astat & SAA7146_PSR_PIN3) { - IDEBUG(printk("stradis%d irq: PIN3\n", saa->nr)); - } - if (astat & SAA7146_PSR_PIN2) { - IDEBUG(printk("stradis%d irq: PIN2\n", saa->nr)); - } - if (astat & SAA7146_PSR_PIN0) { - IDEBUG(printk("stradis%d irq: PIN0\n", saa->nr)); - } - if (astat & SAA7146_PSR_ECS) { - IDEBUG(printk("stradis%d irq: ECS\n", saa->nr)); - } - if (astat & SAA7146_PSR_EC3S) { - IDEBUG(printk("stradis%d irq: EC3S\n", saa->nr)); - } - if (astat & SAA7146_PSR_EC0S) { - IDEBUG(printk("stradis%d irq: EC0S\n", saa->nr)); - } -#endif - count++; - if (count > 15) - printk(KERN_WARNING "stradis%d: irq loop %d\n", - saa->nr, count); - if (count > 20) { - saawrite(0, SAA7146_IER); - printk(KERN_ERR - "stradis%d: IRQ loop cleared\n", saa->nr); - } - } - return IRQ_RETVAL(handled); -} - -static int ibm_send_command(struct saa7146 *saa, - int command, int data, int chain) -{ - int i; - - if (chain) - debiwrite(saa, debNormal, IBM_MP2_COMMAND, (command << 1)| 1,2); - else - debiwrite(saa, debNormal, IBM_MP2_COMMAND, command << 1, 2); - debiwrite(saa, debNormal, IBM_MP2_CMD_DATA, data, 2); - debiwrite(saa, debNormal, IBM_MP2_CMD_STAT, 1, 2); - for (i = 0; i < 100 && - (debiread(saa, debNormal, IBM_MP2_CMD_STAT, 2) & 1); i++) - schedule(); - if (i == 100) - return -1; - return 0; -} - -static void cs4341_setlevel(struct saa7146 *saa, int left, int right) -{ - I2CWrite(saa, 0x22, 0x03, left > 94 ? 94 : left, 2); - I2CWrite(saa, 0x22, 0x04, right > 94 ? 94 : right, 2); -} - -static void initialize_cs4341(struct saa7146 *saa) -{ - int i; - for (i = 0; i < 200; i++) { - /* auto mute off, power on, no de-emphasis */ - /* I2S data up to 24-bit 64xFs internal SCLK */ - I2CWrite(saa, 0x22, 0x01, 0x11, 2); - /* ATAPI mixer settings */ - I2CWrite(saa, 0x22, 0x02, 0x49, 2); - /* attenuation left 3db */ - I2CWrite(saa, 0x22, 0x03, 0x00, 2); - /* attenuation right 3db */ - I2CWrite(saa, 0x22, 0x04, 0x00, 2); - I2CWrite(saa, 0x22, 0x01, 0x10, 2); - if (I2CRead(saa, 0x22, 0x02, 1) == 0x49) - break; - schedule(); - } - printk("stradis%d: CS4341 initialized (%d)\n", saa->nr, i); - return; -} - -static void initialize_cs8420(struct saa7146 *saa, int pro) -{ - int i; - u8 *sequence; - if (pro) - sequence = mode8420pro; - else - sequence = mode8420con; - for (i = 0; i < INIT8420LEN; i++) - I2CWrite(saa, 0x20, init8420[i * 2], init8420[i * 2 + 1], 2); - for (i = 0; i < MODE8420LEN; i++) - I2CWrite(saa, 0x20, sequence[i * 2], sequence[i * 2 + 1], 2); - printk("stradis%d: CS8420 initialized\n", saa->nr); -} - -static void initialize_saa7121(struct saa7146 *saa, int dopal) -{ - int i, mod; - u8 *sequence; - if (dopal) - sequence = init7121pal; - else - sequence = init7121ntsc; - mod = saaread(SAA7146_PSR) & 0x08; - /* initialize PAL/NTSC video encoder */ - for (i = 0; i < INIT7121LEN; i++) { - if (NewCard) { /* handle new card encoder differences */ - if (sequence[i * 2] == 0x3a) - I2CWrite(saa, 0x88, 0x3a, 0x13, 2); - else if (sequence[i * 2] == 0x6b) - I2CWrite(saa, 0x88, 0x6b, 0x20, 2); - else if (sequence[i * 2] == 0x6c) - I2CWrite(saa, 0x88, 0x6c, - dopal ? 0x09 : 0xf5, 2); - else if (sequence[i * 2] == 0x6d) - I2CWrite(saa, 0x88, 0x6d, - dopal ? 0x20 : 0x00, 2); - else if (sequence[i * 2] == 0x7a) - I2CWrite(saa, 0x88, 0x7a, - dopal ? (PALFirstActive - 1) : - (NTSCFirstActive - 4), 2); - else if (sequence[i * 2] == 0x7b) - I2CWrite(saa, 0x88, 0x7b, - dopal ? PALLastActive : - NTSCLastActive, 2); - else - I2CWrite(saa, 0x88, sequence[i * 2], - sequence[i * 2 + 1], 2); - } else { - if (sequence[i * 2] == 0x6b && mod) - I2CWrite(saa, 0x88, 0x6b, - (sequence[i * 2 + 1] ^ 0x09), 2); - else if (sequence[i * 2] == 0x7a) - I2CWrite(saa, 0x88, 0x7a, - dopal ? (PALFirstActive - 1) : - (NTSCFirstActive - 4), 2); - else if (sequence[i * 2] == 0x7b) - I2CWrite(saa, 0x88, 0x7b, - dopal ? PALLastActive : - NTSCLastActive, 2); - else - I2CWrite(saa, 0x88, sequence[i * 2], - sequence[i * 2 + 1], 2); - } - } -} - -static void set_genlock_offset(struct saa7146 *saa, int noffset) -{ - int nCode; - int PixelsPerLine = 858; - if (CurrentMode == VIDEO_MODE_PAL) - PixelsPerLine = 864; - if (noffset > 500) - noffset = 500; - else if (noffset < -500) - noffset = -500; - nCode = noffset + 0x100; - if (nCode == 1) - nCode = 0x401; - else if (nCode < 1) - nCode = 0x400 + PixelsPerLine + nCode; - debiwrite(saa, debNormal, XILINX_GLDELAY, nCode, 2); -} - -static void set_out_format(struct saa7146 *saa, int mode) -{ - initialize_saa7121(saa, (mode == VIDEO_MODE_NTSC ? 0 : 1)); - saa->boardcfg[2] = mode; - /* do not adjust analog video parameters here, use saa7121 init */ - /* you will affect the SDI output on the new card */ - if (mode == VIDEO_MODE_PAL) { /* PAL */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x0808, 2); - mdelay(50); - saawrite(0x012002c0, SAA7146_NUM_LINE_BYTE1); - if (NewCard) { - debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, 0xe100, 2); - mdelay(50); - } - debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, - NewCard ? 0xe500 : 0x6500, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_DLY, - (1 << 8) | - (NewCard ? PALFirstActive : PALFirstActive - 6), 2); - } else { /* NTSC */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x0800, 2); - mdelay(50); - saawrite(0x00f002c0, SAA7146_NUM_LINE_BYTE1); - debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, - NewCard ? 0xe100 : 0x6100, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_DLY, - (1 << 8) | - (NewCard ? NTSCFirstActive : NTSCFirstActive - 6), 2); - } -} - -/* Intialize bitmangler to map from a byte value to the mangled word that - * must be output to program the Xilinx part through the DEBI port. - * Xilinx Data Bit->DEBI Bit: 0->15 1->7 2->6 3->12 4->11 5->2 6->1 7->0 - * transfer FPGA code, init IBM chip, transfer IBM microcode - * rev2 card mangles: 0->7 1->6 2->5 3->4 4->3 5->2 6->1 7->0 - */ -static u16 bitmangler[256]; - -static int initialize_fpga(struct video_code *bitdata) -{ - int i, num, startindex, failure = 0, loadtwo, loadfile = 0; - u16 *dmabuf; - u8 *newdma; - struct saa7146 *saa; - - /* verify fpga code */ - for (startindex = 0; startindex < bitdata->datasize; startindex++) - if (bitdata->data[startindex] == 255) - break; - if (startindex == bitdata->datasize) { - printk(KERN_INFO "stradis: bad fpga code\n"); - return -1; - } - /* initialize all detected cards */ - for (num = 0; num < saa_num; num++) { - saa = &saa7146s[num]; - if (saa->boardcfg[0] > 20) - continue; /* card was programmed */ - loadtwo = (saa->boardcfg[18] & 0x10); - if (!NewCard) /* we have an old board */ - for (i = 0; i < 256; i++) - bitmangler[i] = ((i & 0x01) << 15) | - ((i & 0x02) << 6) | ((i & 0x04) << 4) | - ((i & 0x08) << 9) | ((i & 0x10) << 7) | - ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | - ((i & 0x80) >> 7); - else /* else we have a new board */ - for (i = 0; i < 256; i++) - bitmangler[i] = ((i & 0x01) << 7) | - ((i & 0x02) << 5) | ((i & 0x04) << 3) | - ((i & 0x08) << 1) | ((i & 0x10) >> 1) | - ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | - ((i & 0x80) >> 7); - - dmabuf = (u16 *) saa->dmadebi; - newdma = (u8 *) saa->dmadebi; - if (NewCard) { /* SDM2xxx */ - if (!strncmp(bitdata->loadwhat, "decoder2", 8)) - continue; /* fpga not for this card */ - if (!strncmp(&saa->boardcfg[42], bitdata->loadwhat, 8)) - loadfile = 1; - else if (loadtwo && !strncmp(&saa->boardcfg[19], - bitdata->loadwhat, 8)) - loadfile = 2; - else if (!saa->boardcfg[42] && !strncmp("decxl", - bitdata->loadwhat, 8)) - loadfile = 1; /* special */ - else - continue; /* fpga not for this card */ - if (loadfile != 1 && loadfile != 2) - continue; /* skip to next card */ - if (saa->boardcfg[0] && loadfile == 1) - continue; /* skip to next card */ - if (saa->boardcfg[0] != 1 && loadfile == 2) - continue; /* skip to next card */ - saa->boardcfg[0]++; /* mark fpga handled */ - printk("stradis%d: loading %s\n", saa->nr, - bitdata->loadwhat); - if (loadtwo && loadfile == 2) - goto send_fpga_stuff; - /* turn on the Audio interface to set PROG low */ - saawrite(0x00400040, SAA7146_GPIO_CTRL); - saaread(SAA7146_PSR); /* ensure posted write */ - /* wait for everyone to reset */ - mdelay(10); - saawrite(0x00400000, SAA7146_GPIO_CTRL); - } else { /* original card */ - if (strncmp(bitdata->loadwhat, "decoder2", 8)) - continue; /* fpga not for this card */ - /* Pull the Xilinx PROG signal WS3 low */ - saawrite(0x02000200, SAA7146_MC1); - /* Turn on the Audio interface so can set PROG low */ - saawrite(0x000000c0, SAA7146_ACON1); - /* Pull the Xilinx INIT signal (GPIO2) low */ - saawrite(0x00400000, SAA7146_GPIO_CTRL); - /* Make sure everybody resets */ - saaread(SAA7146_PSR); /* ensure posted write */ - mdelay(10); - /* Release the Xilinx PROG signal */ - saawrite(0x00000000, SAA7146_ACON1); - /* Turn off the Audio interface */ - saawrite(0x02000000, SAA7146_MC1); - } - /* Release Xilinx INIT signal (WS2) */ - saawrite(0x00000000, SAA7146_GPIO_CTRL); - /* Wait for the INIT to go High */ - for (i = 0; - i < 10000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); - i++) - schedule(); - if (i == 1000) { - printk(KERN_INFO "stradis%d: no fpga INIT\n", saa->nr); - return -1; - } -send_fpga_stuff: - if (NewCard) { - for (i = startindex; i < bitdata->datasize; i++) - newdma[i - startindex] = - bitmangler[bitdata->data[i]]; - debiwrite(saa, 0x01420000, 0, 0, - ((bitdata->datasize - startindex) + 5)); - if (loadtwo && loadfile == 1) { - printk("stradis%d: awaiting 2nd FPGA bitfile\n", - saa->nr); - continue; /* skip to next card */ - } - } else { - for (i = startindex; i < bitdata->datasize; i++) - dmabuf[i - startindex] = - bitmangler[bitdata->data[i]]; - debiwrite(saa, 0x014a0000, 0, 0, - ((bitdata->datasize - startindex) + 5) * 2); - } - for (i = 0; - i < 1000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); - i++) - schedule(); - if (i == 1000) { - printk(KERN_INFO "stradis%d: FPGA load failed\n", - saa->nr); - failure++; - continue; - } - if (!NewCard) { - /* Pull the Xilinx INIT signal (GPIO2) low */ - saawrite(0x00400000, SAA7146_GPIO_CTRL); - saaread(SAA7146_PSR); /* ensure posted write */ - mdelay(2); - saawrite(0x00000000, SAA7146_GPIO_CTRL); - mdelay(2); - } - printk(KERN_INFO "stradis%d: FPGA Loaded\n", saa->nr); - saa->boardcfg[0] = 26; /* mark fpga programmed */ - /* set VXCO to its lowest frequency */ - debiwrite(saa, debNormal, XILINX_PWM, 0, 2); - if (NewCard) { - /* mute CS3310 */ - if (HaveCS3310) - debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, - 0, 2); - /* set VXCO to PWM mode, release reset, blank on */ - debiwrite(saa, debNormal, XILINX_CTL0, 0xffc4, 2); - mdelay(10); - /* unmute CS3310 */ - if (HaveCS3310) - debiwrite(saa, debNormal, XILINX_CTL0, - 0x2020, 2); - } - /* set source Black */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2); - saa->boardcfg[4] = 22; /* set NTSC First Active Line */ - saa->boardcfg[5] = 23; /* set PAL First Active Line */ - saa->boardcfg[54] = 2; /* set NTSC Last Active Line - 256 */ - saa->boardcfg[55] = 54; /* set PAL Last Active Line - 256 */ - set_out_format(saa, VIDEO_MODE_NTSC); - mdelay(50); - /* begin IBM chip init */ - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 4, 2); - saaread(SAA7146_PSR); /* wait for reset */ - mdelay(5); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0, 2); - debiread(saa, debNormal, IBM_MP2_CHIP_CONTROL, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0x10, 2); - debiwrite(saa, debNormal, IBM_MP2_CMD_ADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_MODE, 0x2e, 2); - if (NewCard) { - mdelay(5); - /* set i2s rate converter to 48KHz */ - debiwrite(saa, debNormal, 0x80c0, 6, 2); - /* we must init CS8420 first since rev b pulls i2s */ - /* master clock low and CS4341 needs i2s master to */ - /* run the i2c port. */ - if (HaveCS8420) - /* 0=consumer, 1=pro */ - initialize_cs8420(saa, 0); - - mdelay(5); - if (HaveCS4341) - initialize_cs4341(saa); - } - debiwrite(saa, debNormal, IBM_MP2_INFC_CTL, 0x48, 2); - debiwrite(saa, debNormal, IBM_MP2_BEEP_CTL, 0xa000, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_LBOR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_TBOR, 0, 2); - if (NewCard) - set_genlock_offset(saa, 0); - debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2); -#if 0 - /* enable genlock */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x8000, 2); -#else - /* disable genlock */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x8080, 2); -#endif - } - - return failure; -} - -static int do_ibm_reset(struct saa7146 *saa) -{ - /* failure if decoder not previously programmed */ - if (saa->boardcfg[0] < 37) - return -EIO; - /* mute CS3310 */ - if (HaveCS3310) - debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, 0, 2); - /* disable interrupts */ - saawrite(0, SAA7146_IER); - saa->audhead = saa->audtail = 0; - saa->vidhead = saa->vidtail = 0; - /* tristate debi bus, disable debi transfers */ - saawrite(0x00880000, SAA7146_MC1); - /* ensure posted write */ - saaread(SAA7146_MC1); - mdelay(50); - /* re-enable debi transfers */ - saawrite(0x00880088, SAA7146_MC1); - /* set source Black */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2); - /* begin IBM chip init */ - set_out_format(saa, CurrentMode); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 4, 2); - saaread(SAA7146_PSR); /* wait for reset */ - mdelay(5); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0, 2); - debiread(saa, debNormal, IBM_MP2_CHIP_CONTROL, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_MODE, 0x2e, 2); - if (NewCard) { - mdelay(5); - /* set i2s rate converter to 48KHz */ - debiwrite(saa, debNormal, 0x80c0, 6, 2); - /* we must init CS8420 first since rev b pulls i2s */ - /* master clock low and CS4341 needs i2s master to */ - /* run the i2c port. */ - if (HaveCS8420) - /* 0=consumer, 1=pro */ - initialize_cs8420(saa, 1); - - mdelay(5); - if (HaveCS4341) - initialize_cs4341(saa); - } - debiwrite(saa, debNormal, IBM_MP2_INFC_CTL, 0x48, 2); - debiwrite(saa, debNormal, IBM_MP2_BEEP_CTL, 0xa000, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_LBOR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_TBOR, 0, 2); - if (NewCard) - set_genlock_offset(saa, 0); - debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2); - debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2); - if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER, - (ChipControl == 0x43 ? 0xe800 : 0xe000), 1)) { - printk(KERN_ERR "stradis%d: IBM config failed\n", saa->nr); - } - if (HaveCS3310) { - int i = CS3310MaxLvl; - debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, ((i << 8)| i),2); - } - /* start video decoder */ - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); - /* 256k vid, 3520 bytes aud */ - debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037, 2); - debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2); - ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); - /* enable buffer threshold irq */ - debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); - /* clear pending interrupts */ - debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); - debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2); - - return 0; -} - -/* load the decoder microcode */ -static int initialize_ibmmpeg2(struct video_code *microcode) -{ - int i, num; - struct saa7146 *saa; - - for (num = 0; num < saa_num; num++) { - saa = &saa7146s[num]; - /* check that FPGA is loaded */ - debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0xa55a, 2); - i = debiread(saa, debNormal, IBM_MP2_OSD_SIZE, 2); - if (i != 0xa55a) { - printk(KERN_INFO "stradis%d: %04x != 0xa55a\n", - saa->nr, i); -#if 0 - return -1; -#endif - } - if (!strncmp(microcode->loadwhat, "decoder.vid", 11)) { - if (saa->boardcfg[0] > 27) - continue; /* skip to next card */ - /* load video control store */ - saa->boardcfg[1] = 0x13; /* no-sync default */ - debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2); - debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2); - for (i = 0; i < microcode->datasize / 2; i++) - debiwrite(saa, debNormal, IBM_MP2_PROC_IDATA, - (microcode->data[i * 2] << 8) | - microcode->data[i * 2 + 1], 2); - debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - saa->boardcfg[0] = 28; - } - if (!strncmp(microcode->loadwhat, "decoder.aud", 11)) { - if (saa->boardcfg[0] > 35) - continue; /* skip to next card */ - /* load audio control store */ - debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2); - debiwrite(saa, debNormal, IBM_MP2_AUD_IADDR, 0, 2); - for (i = 0; i < microcode->datasize; i++) - debiwrite(saa, debNormal, IBM_MP2_AUD_IDATA, - microcode->data[i], 1); - debiwrite(saa, debNormal, IBM_MP2_AUD_IADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2); - debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2); - if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER, - 0xe000, 1)) { - printk(KERN_ERR "stradis%d: IBM config " - "failed\n", saa->nr); - return -1; - } - /* set PWM to center value */ - if (NewCard) { - debiwrite(saa, debNormal, XILINX_PWM, - saa->boardcfg[14] + - (saa->boardcfg[13] << 8), 2); - } else - debiwrite(saa, debNormal, XILINX_PWM, 0x46, 2); - - if (HaveCS3310) { - i = CS3310MaxLvl; - debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, - (i << 8) | i, 2); - } - printk(KERN_INFO "stradis%d: IBM MPEGCD%d Inited\n", - saa->nr, 18 + (debiread(saa, debNormal, - IBM_MP2_CHIP_CONTROL, 2) >> 12)); - /* start video decoder */ - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037, - 2); /* 256k vid, 3520 bytes aud */ - debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2); - ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); - /* enable buffer threshold irq */ - debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); - debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); - /* enable gpio irq */ - saawrite(0x00002000, SAA7146_GPIO_CTRL); - /* enable decoder output to HPS */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2); - saa->boardcfg[0] = 37; - } - } - - return 0; -} - -static u32 palette2fmt[] = { /* some of these YUV translations are wrong */ - 0xffffffff, 0x86000000, 0x87000000, 0x80000000, 0x8100000, 0x82000000, - 0x83000000, 0x00000000, 0x03000000, 0x03000000, 0x0a00000, 0x03000000, - 0x06000000, 0x00000000, 0x03000000, 0x0a000000, 0x0300000 -}; -static int bpp2fmt[4] = { - VIDEO_PALETTE_HI240, VIDEO_PALETTE_RGB565, VIDEO_PALETTE_RGB24, - VIDEO_PALETTE_RGB32 -}; - -/* I wish I could find a formula to calculate these... */ -static u32 h_prescale[64] = { - 0x10000000, 0x18040202, 0x18080000, 0x380c0606, 0x38100204, 0x38140808, - 0x38180000, 0x381c0000, 0x3820161c, 0x38242a3b, 0x38281230, 0x382c4460, - 0x38301040, 0x38340080, 0x38380000, 0x383c0000, 0x3840fefe, 0x3844ee9f, - 0x3848ee9f, 0x384cee9f, 0x3850ee9f, 0x38542a3b, 0x38581230, 0x385c0000, - 0x38600000, 0x38640000, 0x38680000, 0x386c0000, 0x38700000, 0x38740000, - 0x38780000, 0x387c0000, 0x30800000, 0x38840000, 0x38880000, 0x388c0000, - 0x38900000, 0x38940000, 0x38980000, 0x389c0000, 0x38a00000, 0x38a40000, - 0x38a80000, 0x38ac0000, 0x38b00000, 0x38b40000, 0x38b80000, 0x38bc0000, - 0x38c00000, 0x38c40000, 0x38c80000, 0x38cc0000, 0x38d00000, 0x38d40000, - 0x38d80000, 0x38dc0000, 0x38e00000, 0x38e40000, 0x38e80000, 0x38ec0000, - 0x38f00000, 0x38f40000, 0x38f80000, 0x38fc0000, -}; -static u32 v_gain[64] = { - 0x016000ff, 0x016100ff, 0x016100ff, 0x016200ff, 0x016200ff, 0x016200ff, - 0x016200ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, - 0x016300ff, 0x016300ff, 0x016300ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, -}; - -static void saa7146_set_winsize(struct saa7146 *saa) -{ - u32 format; - int offset, yacl, ysci; - saa->win.color_fmt = format = - (saa->win.depth == 15) ? palette2fmt[VIDEO_PALETTE_RGB555] : - palette2fmt[bpp2fmt[(saa->win.bpp - 1) & 3]]; - offset = saa->win.x * saa->win.bpp + saa->win.y * saa->win.bpl; - saawrite(saa->win.vidadr + offset, SAA7146_BASE_EVEN1); - saawrite(saa->win.vidadr + offset + saa->win.bpl, SAA7146_BASE_ODD1); - saawrite(saa->win.bpl * 2, SAA7146_PITCH1); - saawrite(saa->win.vidadr + saa->win.bpl * saa->win.sheight, - SAA7146_PROT_ADDR1); - saawrite(0, SAA7146_PAGE1); - saawrite(format | 0x60, SAA7146_CLIP_FORMAT_CTRL); - offset = (704 / (saa->win.width - 1)) & 0x3f; - saawrite(h_prescale[offset], SAA7146_HPS_H_PRESCALE); - offset = (720896 / saa->win.width) / (offset + 1); - saawrite((offset << 12) | 0x0c, SAA7146_HPS_H_SCALE); - if (CurrentMode == VIDEO_MODE_NTSC) { - yacl = /*(480 / saa->win.height - 1) & 0x3f */ 0; - ysci = 1024 - (saa->win.height * 1024 / 480); - } else { - yacl = /*(576 / saa->win.height - 1) & 0x3f */ 0; - ysci = 1024 - (saa->win.height * 1024 / 576); - } - saawrite((1 << 31) | (ysci << 21) | (yacl << 15), SAA7146_HPS_V_SCALE); - saawrite(v_gain[yacl], SAA7146_HPS_V_GAIN); - saawrite(((SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_HPS_V | - SAA7146_MC2_UPLD_HPS_H) << 16) | (SAA7146_MC2_UPLD_DMA1 | - SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_HPS_H), SAA7146_MC2); -} - -/* clip_draw_rectangle(cm,x,y,w,h) -- handle clipping an area - * bitmap is fixed width, 128 bytes (1024 pixels represented) - * arranged most-sigificant-bit-left in 32-bit words - * based on saa7146 clipping hardware, it swaps bytes if LE - * much of this makes up for egcs brain damage -- so if you - * are wondering "why did he do this?" it is because the C - * was adjusted to generate the optimal asm output without - * writing non-portable __asm__ directives. - */ - -static void clip_draw_rectangle(u32 *clipmap, int x, int y, int w, int h) -{ - register int startword, endword; - register u32 bitsleft, bitsright; - u32 *temp; - if (x < 0) { - w += x; - x = 0; - } - if (y < 0) { - h += y; - y = 0; - } - if (w <= 0 || h <= 0 || x > 1023 || y > 639) - return; /* throw away bad clips */ - if (x + w > 1024) - w = 1024 - x; - if (y + h > 640) - h = 640 - y; - startword = (x >> 5); - endword = ((x + w) >> 5); - bitsleft = (0xffffffff >> (x & 31)); - bitsright = (0xffffffff << (~((x + w) - (endword << 5)))); - temp = &clipmap[(y << 5) + startword]; - w = endword - startword; - if (!w) { - bitsleft |= bitsright; - for (y = 0; y < h; y++) { - *temp |= bitsleft; - temp += 32; - } - } else { - for (y = 0; y < h; y++) { - *temp++ |= bitsleft; - for (x = 1; x < w; x++) - *temp++ = 0xffffffff; - *temp |= bitsright; - temp += (32 - w); - } - } -} - -static void make_clip_tab(struct saa7146 *saa, struct video_clip *cr, int ncr) -{ - int i, width, height; - u32 *clipmap; - - clipmap = saa->dmavid2; - if ((width = saa->win.width) > 1023) - width = 1023; /* sanity check */ - if ((height = saa->win.height) > 640) - height = 639; /* sanity check */ - if (ncr > 0) { /* rectangles pased */ - /* convert rectangular clips to a bitmap */ - memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */ - for (i = 0; i < ncr; i++) - clip_draw_rectangle(clipmap, cr[i].x, cr[i].y, - cr[i].width, cr[i].height); - } - /* clip against viewing window AND screen - so we do not have to rely on the user program - */ - clip_draw_rectangle(clipmap, (saa->win.x + width > saa->win.swidth) ? - (saa->win.swidth - saa->win.x) : width, 0, 1024, 768); - clip_draw_rectangle(clipmap, 0, - (saa->win.y + height > saa->win.sheight) ? - (saa->win.sheight - saa->win.y) : height, 1024, 768); - if (saa->win.x < 0) - clip_draw_rectangle(clipmap, 0, 0, -saa->win.x, 768); - if (saa->win.y < 0) - clip_draw_rectangle(clipmap, 0, 0, 1024, -saa->win.y); -} - -static long saa_ioctl(struct file *file, - unsigned int cmd, unsigned long argl) -{ - struct saa7146 *saa = file->private_data; - void __user *arg = (void __user *)argl; - - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability b; - strcpy(b.name, saa->video_dev.name); - b.type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | - VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM | - VID_TYPE_SCALES; - b.channels = 1; - b.audios = 1; - b.maxwidth = 768; - b.maxheight = 576; - b.minwidth = 32; - b.minheight = 32; - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - return 0; - } - case VIDIOCGPICT: - { - struct video_picture p = saa->picture; - if (saa->win.depth == 8) - p.palette = VIDEO_PALETTE_HI240; - if (saa->win.depth == 15) - p.palette = VIDEO_PALETTE_RGB555; - if (saa->win.depth == 16) - p.palette = VIDEO_PALETTE_RGB565; - if (saa->win.depth == 24) - p.palette = VIDEO_PALETTE_RGB24; - if (saa->win.depth == 32) - p.palette = VIDEO_PALETTE_RGB32; - if (copy_to_user(arg, &p, sizeof(p))) - return -EFAULT; - return 0; - } - case VIDIOCSPICT: - { - struct video_picture p; - u32 format; - if (copy_from_user(&p, arg, sizeof(p))) - return -EFAULT; - if (p.palette < ARRAY_SIZE(palette2fmt)) { - format = palette2fmt[p.palette]; - saa->win.color_fmt = format; - saawrite(format | 0x60, - SAA7146_CLIP_FORMAT_CTRL); - } - saawrite(((p.brightness & 0xff00) << 16) | - ((p.contrast & 0xfe00) << 7) | - ((p.colour & 0xfe00) >> 9), SAA7146_BCS_CTRL); - saa->picture = p; - /* upload changed registers */ - saawrite(((SAA7146_MC2_UPLD_HPS_H | - SAA7146_MC2_UPLD_HPS_V) << 16) | - SAA7146_MC2_UPLD_HPS_H | - SAA7146_MC2_UPLD_HPS_V, SAA7146_MC2); - return 0; - } - case VIDIOCSWIN: - { - struct video_window vw; - struct video_clip *vcp = NULL; - - if (copy_from_user(&vw, arg, sizeof(vw))) - return -EFAULT; - - /* stop capture */ - if (vw.flags || vw.width < 16 || vw.height < 16) { - saawrite((SAA7146_MC1_TR_E_1 << 16), - SAA7146_MC1); - return -EINVAL; - } - /* 32-bit align start and adjust width */ - if (saa->win.bpp < 4) { - int i = vw.x; - vw.x = (vw.x + 3) & ~3; - i = vw.x - i; - vw.width -= i; - } - saa->win.x = vw.x; - saa->win.y = vw.y; - saa->win.width = vw.width; - if (saa->win.width > 768) - saa->win.width = 768; - saa->win.height = vw.height; - if (CurrentMode == VIDEO_MODE_NTSC) { - if (saa->win.height > 480) - saa->win.height = 480; - } else { - if (saa->win.height > 576) - saa->win.height = 576; - } - - /* stop capture */ - saawrite((SAA7146_MC1_TR_E_1 << 16), SAA7146_MC1); - saa7146_set_winsize(saa); - - /* - * Do any clips. - */ - if (vw.clipcount < 0) { - if (copy_from_user(saa->dmavid2, vw.clips, - VIDEO_CLIPMAP_SIZE)) - return -EFAULT; - } else if (vw.clipcount > 16384) { - return -EINVAL; - } else if (vw.clipcount > 0) { - vcp = vmalloc(sizeof(struct video_clip) * - vw.clipcount); - if (vcp == NULL) - return -ENOMEM; - if (copy_from_user(vcp, vw.clips, - sizeof(struct video_clip) * - vw.clipcount)) { - vfree(vcp); - return -EFAULT; - } - } else /* nothing clipped */ - memset(saa->dmavid2, 0, VIDEO_CLIPMAP_SIZE); - - make_clip_tab(saa, vcp, vw.clipcount); - if (vw.clipcount > 0) - vfree(vcp); - - /* start capture & clip dma if we have an address */ - if ((saa->cap & 3) && saa->win.vidadr != 0) - saawrite(((SAA7146_MC1_TR_E_1 | - SAA7146_MC1_TR_E_2) << 16) | 0xffff, - SAA7146_MC1); - return 0; - } - case VIDIOCGWIN: - { - struct video_window vw; - vw.x = saa->win.x; - vw.y = saa->win.y; - vw.width = saa->win.width; - vw.height = saa->win.height; - vw.chromakey = 0; - vw.flags = 0; - if (copy_to_user(arg, &vw, sizeof(vw))) - return -EFAULT; - return 0; - } - case VIDIOCCAPTURE: - { - int v; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if (v == 0) { - saa->cap &= ~1; - saawrite((SAA7146_MC1_TR_E_1 << 16), - SAA7146_MC1); - } else { - if (saa->win.vidadr == 0 || saa->win.width == 0 - || saa->win.height == 0) - return -EINVAL; - saa->cap |= 1; - saawrite((SAA7146_MC1_TR_E_1 << 16) | 0xffff, - SAA7146_MC1); - } - return 0; - } - case VIDIOCGFBUF: - { - struct video_buffer v; - v.base = (void *)saa->win.vidadr; - v.height = saa->win.sheight; - v.width = saa->win.swidth; - v.depth = saa->win.depth; - v.bytesperline = saa->win.bpl; - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; - - } - case VIDIOCSFBUF: - { - struct video_buffer v; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if (v.depth != 8 && v.depth != 15 && v.depth != 16 && - v.depth != 24 && v.depth != 32 && v.width > 16 && - v.height > 16 && v.bytesperline > 16) - return -EINVAL; - if (v.base) - saa->win.vidadr = (unsigned long)v.base; - saa->win.sheight = v.height; - saa->win.swidth = v.width; - saa->win.bpp = ((v.depth + 7) & 0x38) / 8; - saa->win.depth = v.depth; - saa->win.bpl = v.bytesperline; - - DEBUG(printk("Display at %p is %d by %d, bytedepth %d, " - "bpl %d\n", v.base, v.width, v.height, - saa->win.bpp, saa->win.bpl)); - saa7146_set_winsize(saa); - return 0; - } - case VIDIOCKEY: - { - /* Will be handled higher up .. */ - return 0; - } - - case VIDIOCGAUDIO: - { - struct video_audio v; - v = saa->audio_dev; - v.flags &= ~(VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE); - v.flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME; - strcpy(v.name, "MPEG"); - v.mode = VIDEO_SOUND_STEREO; - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; - } - case VIDIOCSAUDIO: - { - struct video_audio v; - int i; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - i = (~(v.volume >> 8)) & 0xff; - if (!HaveCS4341) { - if (v.flags & VIDEO_AUDIO_MUTE) - debiwrite(saa, debNormal, - IBM_MP2_FRNT_ATTEN, 0xffff, 2); - if (!(v.flags & VIDEO_AUDIO_MUTE)) - debiwrite(saa, debNormal, - IBM_MP2_FRNT_ATTEN, 0x0000, 2); - if (v.flags & VIDEO_AUDIO_VOLUME) - debiwrite(saa, debNormal, - IBM_MP2_FRNT_ATTEN, - (i << 8) | i, 2); - } else { - if (v.flags & VIDEO_AUDIO_MUTE) - cs4341_setlevel(saa, 0xff, 0xff); - if (!(v.flags & VIDEO_AUDIO_MUTE)) - cs4341_setlevel(saa, 0, 0); - if (v.flags & VIDEO_AUDIO_VOLUME) - cs4341_setlevel(saa, i, i); - } - saa->audio_dev = v; - return 0; - } - - case VIDIOCGUNIT: - { - struct video_unit vu; - vu.video = saa->video_dev.minor; - vu.vbi = VIDEO_NO_UNIT; - vu.radio = VIDEO_NO_UNIT; - vu.audio = VIDEO_NO_UNIT; - vu.teletext = VIDEO_NO_UNIT; - if (copy_to_user(arg, &vu, sizeof(vu))) - return -EFAULT; - return 0; - } - case VIDIOCSPLAYMODE: - { - struct video_play_mode pmode; - if (copy_from_user((void *)&pmode, arg, - sizeof(struct video_play_mode))) - return -EFAULT; - switch (pmode.mode) { - case VID_PLAY_VID_OUT_MODE: - if (pmode.p1 != VIDEO_MODE_NTSC && - pmode.p1 != VIDEO_MODE_PAL) - return -EINVAL; - set_out_format(saa, pmode.p1); - return 0; - case VID_PLAY_GENLOCK: - debiwrite(saa, debNormal, XILINX_CTL0, - pmode.p1 ? 0x8000 : 0x8080, 2); - if (NewCard) - set_genlock_offset(saa, pmode.p2); - return 0; - case VID_PLAY_NORMAL: - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, ChipControl, 2); - ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_PAUSE: - /* IBM removed the PAUSE command */ - /* they say use SINGLE_FRAME now */ - case VID_PLAY_SINGLE_FRAME: - ibm_send_command(saa, IBM_MP2_SINGLE_FRAME,0,0); - if (saa->playmode == pmode.mode) { - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - } - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_FAST_FORWARD: - ibm_send_command(saa, IBM_MP2_FAST_FORWARD,0,0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_SLOW_MOTION: - ibm_send_command(saa, IBM_MP2_SLOW_MOTION, - pmode.p1, 0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_IMMEDIATE_NORMAL: - /* ensure transfers resume */ - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, ChipControl, 2); - ibm_send_command(saa, IBM_MP2_IMED_NORM_PLAY, - 0, 0); - saa->playmode = VID_PLAY_NORMAL; - return 0; - case VID_PLAY_SWITCH_CHANNELS: - saa->audhead = saa->audtail = 0; - saa->vidhead = saa->vidtail = 0; - ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,1); - ibm_send_command(saa, IBM_MP2_RESET_AUD_RATE, - 0, 1); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, - 0, 2); - ibm_send_command(saa, IBM_MP2_CHANNEL_SWITCH, - 0, 1); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); - saa->playmode = VID_PLAY_NORMAL; - return 0; - case VID_PLAY_FREEZE_FRAME: - ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_STILL_MODE: - ibm_send_command(saa, IBM_MP2_SET_STILL_MODE, - 0, 0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_MASTER_MODE: - if (pmode.p1 == VID_PLAY_MASTER_NONE) - saa->boardcfg[1] = 0x13; - else if (pmode.p1 == VID_PLAY_MASTER_VIDEO) - saa->boardcfg[1] = 0x23; - else if (pmode.p1 == VID_PLAY_MASTER_AUDIO) - saa->boardcfg[1] = 0x43; - else - return -EINVAL; - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, ChipControl, 2); - return 0; - case VID_PLAY_ACTIVE_SCANLINES: - if (CurrentMode == VIDEO_MODE_PAL) { - if (pmode.p1 < 1 || pmode.p2 > 625) - return -EINVAL; - saa->boardcfg[5] = pmode.p1; - saa->boardcfg[55] = (pmode.p1 + - (pmode.p2 / 2) - 1) & 0xff; - } else { - if (pmode.p1 < 4 || pmode.p2 > 525) - return -EINVAL; - saa->boardcfg[4] = pmode.p1; - saa->boardcfg[54] = (pmode.p1 + - (pmode.p2 / 2) - 4) & 0xff; - } - set_out_format(saa, CurrentMode); - case VID_PLAY_RESET: - return do_ibm_reset(saa); - case VID_PLAY_END_MARK: - if (saa->endmarktail < saa->endmarkhead) { - if (saa->endmarkhead - - saa->endmarktail < 2) - return -ENOSPC; - } else if (saa->endmarkhead <=saa->endmarktail){ - if (saa->endmarktail - saa->endmarkhead - > (MAX_MARKS - 2)) - return -ENOSPC; - } else - return -ENOSPC; - saa->endmark[saa->endmarktail] = saa->audtail; - saa->endmarktail++; - if (saa->endmarktail >= MAX_MARKS) - saa->endmarktail = 0; - } - return -EINVAL; - } - case VIDIOCSWRITEMODE: - { - int mode; - if (copy_from_user((void *)&mode, arg, sizeof(int))) - return -EFAULT; - if (mode == VID_WRITE_MPEG_AUD || - mode == VID_WRITE_MPEG_VID || - mode == VID_WRITE_CC || - mode == VID_WRITE_TTX || - mode == VID_WRITE_OSD) { - saa->writemode = mode; - return 0; - } - return -EINVAL; - } - case VIDIOCSMICROCODE: - { - struct video_code ucode; - __u8 *udata; - int i; - if (copy_from_user(&ucode, arg, sizeof(ucode))) - return -EFAULT; - if (ucode.datasize > 65536 || ucode.datasize < 1024 || - strncmp(ucode.loadwhat, "dec", 3)) - return -EINVAL; - if ((udata = vmalloc(ucode.datasize)) == NULL) - return -ENOMEM; - if (copy_from_user(udata, ucode.data, ucode.datasize)) { - vfree(udata); - return -EFAULT; - } - ucode.data = udata; - if (!strncmp(ucode.loadwhat, "decoder.aud", 11) || - !strncmp(ucode.loadwhat, "decoder.vid", 11)) - i = initialize_ibmmpeg2(&ucode); - else - i = initialize_fpga(&ucode); - vfree(udata); - if (i) - return -EINVAL; - return 0; - - } - case VIDIOCGCHAN: /* this makes xawtv happy */ - { - struct video_channel v; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - v.flags = VIDEO_VC_AUDIO; - v.tuners = 0; - v.type = VID_TYPE_MPEG_DECODER; - v.norm = CurrentMode; - strcpy(v.name, "MPEG2"); - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; - } - case VIDIOCSCHAN: /* this makes xawtv happy */ - { - struct video_channel v; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - /* do nothing */ - return 0; - } - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static int saa_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct saa7146 *saa = file->private_data; - printk(KERN_DEBUG "stradis%d: saa_mmap called\n", saa->nr); - return -EINVAL; -} - -static ssize_t saa_read(struct file *file, char __user * buf, - size_t count, loff_t * ppos) -{ - return -EINVAL; -} - -static ssize_t saa_write(struct file *file, const char __user * buf, - size_t count, loff_t * ppos) -{ - struct saa7146 *saa = file->private_data; - unsigned long todo = count; - int blocksize, split; - unsigned long flags; - - while (todo > 0) { - if (saa->writemode == VID_WRITE_MPEG_AUD) { - spin_lock_irqsave(&saa->lock, flags); - if (saa->audhead <= saa->audtail) - blocksize = 65536 - - (saa->audtail - saa->audhead); - else - blocksize = saa->audhead - saa->audtail; - spin_unlock_irqrestore(&saa->lock, flags); - if (blocksize < 16384) { - saawrite(SAA7146_PSR_DEBI_S | - SAA7146_PSR_PIN1, SAA7146_IER); - saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); - /* wait for buffer space to open */ - interruptible_sleep_on(&saa->audq); - } - spin_lock_irqsave(&saa->lock, flags); - if (saa->audhead <= saa->audtail) { - blocksize = 65536 - - (saa->audtail - saa->audhead); - split = 65536 - saa->audtail; - } else { - blocksize = saa->audhead - saa->audtail; - split = 65536; - } - spin_unlock_irqrestore(&saa->lock, flags); - blocksize--; - if (blocksize > todo) - blocksize = todo; - /* double check that we really have space */ - if (!blocksize) - return -ENOSPC; - if (split < blocksize) { - if (copy_from_user(saa->audbuf + - saa->audtail, buf, split)) - return -EFAULT; - buf += split; - todo -= split; - blocksize -= split; - saa->audtail = 0; - } - if (copy_from_user(saa->audbuf + saa->audtail, buf, - blocksize)) - return -EFAULT; - saa->audtail += blocksize; - todo -= blocksize; - buf += blocksize; - saa->audtail &= 0xffff; - } else if (saa->writemode == VID_WRITE_MPEG_VID) { - spin_lock_irqsave(&saa->lock, flags); - if (saa->vidhead <= saa->vidtail) - blocksize = 524288 - - (saa->vidtail - saa->vidhead); - else - blocksize = saa->vidhead - saa->vidtail; - spin_unlock_irqrestore(&saa->lock, flags); - if (blocksize < 65536) { - saawrite(SAA7146_PSR_DEBI_S | - SAA7146_PSR_PIN1, SAA7146_IER); - saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); - /* wait for buffer space to open */ - interruptible_sleep_on(&saa->vidq); - } - spin_lock_irqsave(&saa->lock, flags); - if (saa->vidhead <= saa->vidtail) { - blocksize = 524288 - - (saa->vidtail - saa->vidhead); - split = 524288 - saa->vidtail; - } else { - blocksize = saa->vidhead - saa->vidtail; - split = 524288; - } - spin_unlock_irqrestore(&saa->lock, flags); - blocksize--; - if (blocksize > todo) - blocksize = todo; - /* double check that we really have space */ - if (!blocksize) - return -ENOSPC; - if (split < blocksize) { - if (copy_from_user(saa->vidbuf + - saa->vidtail, buf, split)) - return -EFAULT; - buf += split; - todo -= split; - blocksize -= split; - saa->vidtail = 0; - } - if (copy_from_user(saa->vidbuf + saa->vidtail, buf, - blocksize)) - return -EFAULT; - saa->vidtail += blocksize; - todo -= blocksize; - buf += blocksize; - saa->vidtail &= 0x7ffff; - } else if (saa->writemode == VID_WRITE_OSD) { - if (count > 131072) - return -ENOSPC; - if (copy_from_user(saa->osdbuf, buf, count)) - return -EFAULT; - buf += count; - saa->osdhead = 0; - saa->osdtail = count; - debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_OSD_LINK_ADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00d, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, - debiread(saa, debNormal, - IBM_MP2_DISP_MODE, 2) | 1, 2); - /* trigger osd data transfer */ - saawrite(SAA7146_PSR_DEBI_S | - SAA7146_PSR_PIN1, SAA7146_IER); - saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); - } - } - return count; -} - -static int saa_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct saa7146 *saa = container_of(vdev, struct saa7146, video_dev); - - lock_kernel(); - file->private_data = saa; - - saa->user++; - if (saa->user > 1) { - unlock_kernel(); - return 0; /* device open already, don't reset */ - } - saa->writemode = VID_WRITE_MPEG_VID; /* default to video */ - unlock_kernel(); - return 0; -} - -static int saa_release(struct file *file) -{ - struct saa7146 *saa = file->private_data; - saa->user--; - - if (saa->user > 0) /* still someone using device */ - return 0; - saawrite(0x007f0000, SAA7146_MC1); /* stop all overlay dma */ - return 0; -} - -static const struct v4l2_file_operations saa_fops = { - .owner = THIS_MODULE, - .open = saa_open, - .release = saa_release, - .ioctl = saa_ioctl, - .read = saa_read, - .write = saa_write, - .mmap = saa_mmap, -}; - -/* template for video_device-structure */ -static struct video_device saa_template = { - .name = "SAA7146A", - .fops = &saa_fops, - .release = video_device_release_empty, -}; - -static int __devinit configure_saa7146(struct pci_dev *pdev, int num) -{ - int retval; - struct saa7146 *saa = pci_get_drvdata(pdev); - - saa->endmarkhead = saa->endmarktail = 0; - saa->win.x = saa->win.y = 0; - saa->win.width = saa->win.cropwidth = 720; - saa->win.height = saa->win.cropheight = 480; - saa->win.cropx = saa->win.cropy = 0; - saa->win.bpp = 2; - saa->win.depth = 16; - saa->win.color_fmt = palette2fmt[VIDEO_PALETTE_RGB565]; - saa->win.bpl = 1024 * saa->win.bpp; - saa->win.swidth = 1024; - saa->win.sheight = 768; - saa->picture.brightness = 32768; - saa->picture.contrast = 38768; - saa->picture.colour = 32768; - saa->cap = 0; - saa->nr = num; - saa->playmode = VID_PLAY_NORMAL; - memset(saa->boardcfg, 0, 64); /* clear board config area */ - saa->saa7146_mem = NULL; - saa->dmavid1 = saa->dmavid2 = saa->dmavid3 = saa->dmaa1in = - saa->dmaa1out = saa->dmaa2in = saa->dmaa2out = - saa->pagevid1 = saa->pagevid2 = saa->pagevid3 = saa->pagea1in = - saa->pagea1out = saa->pagea2in = saa->pagea2out = - saa->pagedebi = saa->dmaRPS1 = saa->dmaRPS2 = saa->pageRPS1 = - saa->pageRPS2 = NULL; - saa->audbuf = saa->vidbuf = saa->osdbuf = saa->dmadebi = NULL; - saa->audhead = saa->vidtail = 0; - - init_waitqueue_head(&saa->i2cq); - init_waitqueue_head(&saa->audq); - init_waitqueue_head(&saa->debiq); - init_waitqueue_head(&saa->vidq); - spin_lock_init(&saa->lock); - - retval = pci_enable_device(pdev); - if (retval) { - dev_err(&pdev->dev, "%d: pci_enable_device failed!\n", num); - goto err; - } - - saa->id = pdev->device; - saa->irq = pdev->irq; - saa->saa7146_adr = pci_resource_start(pdev, 0); - pci_read_config_byte(pdev, PCI_CLASS_REVISION, &saa->revision); - - saa->saa7146_mem = ioremap(saa->saa7146_adr, 0x200); - if (saa->saa7146_mem == NULL) { - dev_err(&pdev->dev, "%d: ioremap failed!\n", num); - retval = -EIO; - goto err; - } - - memcpy(&saa->video_dev, &saa_template, sizeof(saa_template)); - saawrite(0, SAA7146_IER); /* turn off all interrupts */ - - retval = request_irq(saa->irq, saa7146_irq, IRQF_SHARED | IRQF_DISABLED, - "stradis", saa); - if (retval == -EINVAL) - dev_err(&pdev->dev, "%d: Bad irq number or handler\n", num); - else if (retval == -EBUSY) - dev_err(&pdev->dev, "%d: IRQ %ld busy, change your PnP config " - "in BIOS\n", num, saa->irq); - if (retval < 0) - goto errio; - - pci_set_master(pdev); - retval = video_register_device(&saa->video_dev, VFL_TYPE_GRABBER, - video_nr); - if (retval < 0) { - dev_err(&pdev->dev, "%d: error in registering video device!\n", - num); - goto errio; - } - - return 0; -errio: - iounmap(saa->saa7146_mem); -err: - return retval; -} - -static int __devinit init_saa7146(struct pci_dev *pdev) -{ - struct saa7146 *saa = pci_get_drvdata(pdev); - - saa->user = 0; - /* reset the saa7146 */ - saawrite(0xffff0000, SAA7146_MC1); - mdelay(5); - /* enable debi and i2c transfers and pins */ - saawrite(((SAA7146_MC1_EDP | SAA7146_MC1_EI2C | - SAA7146_MC1_TR_E_DEBI) << 16) | 0xffff, SAA7146_MC1); - /* ensure proper state of chip */ - saawrite(0x00000000, SAA7146_PAGE1); - saawrite(0x00f302c0, SAA7146_NUM_LINE_BYTE1); - saawrite(0x00000000, SAA7146_PAGE2); - saawrite(0x01400080, SAA7146_NUM_LINE_BYTE2); - saawrite(0x00000000, SAA7146_DD1_INIT); - saawrite(0x00000000, SAA7146_DD1_STREAM_B); - saawrite(0x00000000, SAA7146_DD1_STREAM_A); - saawrite(0x00000000, SAA7146_BRS_CTRL); - saawrite(0x80400040, SAA7146_BCS_CTRL); - saawrite(0x0000e000 /*| (1<<29) */ , SAA7146_HPS_CTRL); - saawrite(0x00000060, SAA7146_CLIP_FORMAT_CTRL); - saawrite(0x00000000, SAA7146_ACON1); - saawrite(0x00000000, SAA7146_ACON2); - saawrite(0x00000600, SAA7146_I2C_STATUS); - saawrite(((SAA7146_MC2_UPLD_D1_B | SAA7146_MC2_UPLD_D1_A | - SAA7146_MC2_UPLD_BRS | SAA7146_MC2_UPLD_HPS_H | - SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_DMA2 | - SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_I2C) << 16) | 0xffff, - SAA7146_MC2); - /* setup arbitration control registers */ - saawrite(0x1412121a, SAA7146_PCI_BT_V1); - - /* allocate 32k dma buffer + 4k for page table */ - if ((saa->dmadebi = kmalloc(32768 + 4096, GFP_KERNEL)) == NULL) { - dev_err(&pdev->dev, "%d: debi kmalloc failed\n", saa->nr); - goto err; - } -#if 0 - saa->pagedebi = saa->dmadebi + 32768; /* top 4k is for mmu */ - saawrite(virt_to_bus(saa->pagedebi) /*|0x800 */ , SAA7146_DEBI_PAGE); - for (i = 0; i < 12; i++) /* setup mmu page table */ - saa->pagedebi[i] = virt_to_bus((saa->dmadebi + i * 4096)); -#endif - saa->audhead = saa->vidhead = saa->osdhead = 0; - saa->audtail = saa->vidtail = saa->osdtail = 0; - if (saa->vidbuf == NULL && (saa->vidbuf = vmalloc(524288)) == NULL) { - dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); - goto err; - } - if (saa->audbuf == NULL && (saa->audbuf = vmalloc(65536)) == NULL) { - dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); - goto errfree; - } - if (saa->osdbuf == NULL && (saa->osdbuf = vmalloc(131072)) == NULL) { - dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); - goto errfree; - } - /* allocate 81920 byte buffer for clipping */ - if ((saa->dmavid2 = kzalloc(VIDEO_CLIPMAP_SIZE, GFP_KERNEL)) == NULL) { - dev_err(&pdev->dev, "%d: clip kmalloc failed\n", saa->nr); - goto errfree; - } - /* setup clipping registers */ - saawrite(virt_to_bus(saa->dmavid2), SAA7146_BASE_EVEN2); - saawrite(virt_to_bus(saa->dmavid2) + 128, SAA7146_BASE_ODD2); - saawrite(virt_to_bus(saa->dmavid2) + VIDEO_CLIPMAP_SIZE, - SAA7146_PROT_ADDR2); - saawrite(256, SAA7146_PITCH2); - saawrite(4, SAA7146_PAGE2); /* dma direction: read, no byteswap */ - saawrite(((SAA7146_MC2_UPLD_DMA2) << 16) | SAA7146_MC2_UPLD_DMA2, - SAA7146_MC2); - I2CBusScan(saa); - - return 0; -errfree: - vfree(saa->osdbuf); - vfree(saa->audbuf); - vfree(saa->vidbuf); - saa->audbuf = saa->osdbuf = saa->vidbuf = NULL; -err: - return -ENOMEM; -} - -static void stradis_release_saa(struct pci_dev *pdev) -{ - u8 command; - struct saa7146 *saa = pci_get_drvdata(pdev); - - /* turn off all capturing, DMA and IRQs */ - saawrite(0xffff0000, SAA7146_MC1); /* reset chip */ - saawrite(0, SAA7146_MC2); - saawrite(0, SAA7146_IER); - saawrite(0xffffffffUL, SAA7146_ISR); - - /* disable PCI bus-mastering */ - pci_read_config_byte(pdev, PCI_COMMAND, &command); - command &= ~PCI_COMMAND_MASTER; - pci_write_config_byte(pdev, PCI_COMMAND, command); - - /* unmap and free memory */ - saa->audhead = saa->audtail = saa->osdhead = 0; - saa->vidhead = saa->vidtail = saa->osdtail = 0; - vfree(saa->vidbuf); - vfree(saa->audbuf); - vfree(saa->osdbuf); - kfree(saa->dmavid2); - saa->audbuf = saa->vidbuf = saa->osdbuf = NULL; - saa->dmavid2 = NULL; - kfree(saa->dmadebi); - kfree(saa->dmavid1); - kfree(saa->dmavid3); - kfree(saa->dmaa1in); - kfree(saa->dmaa1out); - kfree(saa->dmaa2in); - kfree(saa->dmaa2out); - kfree(saa->dmaRPS1); - kfree(saa->dmaRPS2); - free_irq(saa->irq, saa); - if (saa->saa7146_mem) - iounmap(saa->saa7146_mem); - if (video_is_registered(&saa->video_dev)) - video_unregister_device(&saa->video_dev); -} - -static int __devinit stradis_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int retval = -EINVAL; - - if (saa_num >= SAA7146_MAX) - goto err; - - if (!pdev->subsystem_vendor) - dev_info(&pdev->dev, "%d: rev1 decoder\n", saa_num); - else - dev_info(&pdev->dev, "%d: SDM2xx found\n", saa_num); - - pci_set_drvdata(pdev, &saa7146s[saa_num]); - - retval = configure_saa7146(pdev, saa_num); - if (retval) { - dev_err(&pdev->dev, "%d: error in configuring\n", saa_num); - goto err; - } - - if (init_saa7146(pdev) < 0) { - dev_err(&pdev->dev, "%d: error in initialization\n", saa_num); - retval = -EIO; - goto errrel; - } - - saa_num++; - - return 0; -errrel: - stradis_release_saa(pdev); -err: - return retval; -} - -static void __devexit stradis_remove(struct pci_dev *pdev) -{ - stradis_release_saa(pdev); -} - -static struct pci_device_id stradis_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146) }, - { 0 } -}; - - -static struct pci_driver stradis_driver = { - .name = "stradis", - .id_table = stradis_pci_tbl, - .probe = stradis_probe, - .remove = __devexit_p(stradis_remove) -}; - -static int __init stradis_init(void) -{ - int retval; - - saa_num = 0; - - retval = pci_register_driver(&stradis_driver); - if (retval) - printk(KERN_ERR "stradis: Unable to register pci driver.\n"); - - return retval; -} - -static void __exit stradis_exit(void) -{ - pci_unregister_driver(&stradis_driver); - printk(KERN_INFO "stradis: module cleanup complete\n"); -} - -module_init(stradis_init); -module_exit(stradis_exit); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 303c52cfe915..078525ca19d0 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -53,6 +53,8 @@ source "drivers/staging/tm6000/Kconfig" source "drivers/staging/cpia/Kconfig" +source "drivers/staging/stradis/Kconfig" + source "drivers/staging/usbip/Kconfig" source "drivers/staging/winbond/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index ddcac245c99a..057adca4d04d 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_VIDEO_CPIA) += cpia/ +obj-$(CONFIG_VIDEO_STRADIS) += stradis/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ obj-$(CONFIG_W35UND) += winbond/ diff --git a/drivers/staging/stradis/Kconfig b/drivers/staging/stradis/Kconfig new file mode 100644 index 000000000000..92e891141896 --- /dev/null +++ b/drivers/staging/stradis/Kconfig @@ -0,0 +1,7 @@ +config VIDEO_STRADIS + tristate "Stradis 4:2:2 MPEG-2 video driver (DEPRECATED)" + depends on EXPERIMENTAL && PCI && VIDEO_V4L1 && VIRT_TO_BUS + help + Say Y here to enable support for the Stradis 4:2:2 MPEG-2 video + driver for PCI. There is a product page at + . diff --git a/drivers/staging/stradis/Makefile b/drivers/staging/stradis/Makefile new file mode 100644 index 000000000000..0f1feab59e39 --- /dev/null +++ b/drivers/staging/stradis/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_VIDEO_STRADIS) += stradis.o + +EXTRA_CFLAGS += -Idrivers/media/video diff --git a/drivers/staging/stradis/TODO b/drivers/staging/stradis/TODO new file mode 100644 index 000000000000..f48150fe2fa9 --- /dev/null +++ b/drivers/staging/stradis/TODO @@ -0,0 +1,6 @@ +This is an obsolete driver for ancient stradis hardware. +We couldn't find anyone with this hardware in order to port it to use V4L2. + +If nobody take care on it, the driver will be removed for 2.6.38. + +Please send patches to linux-media@vger.kernel.org diff --git a/drivers/staging/stradis/stradis.c b/drivers/staging/stradis/stradis.c new file mode 100644 index 000000000000..a057824e7ebc --- /dev/null +++ b/drivers/staging/stradis/stradis.c @@ -0,0 +1,2213 @@ +/* + * stradis.c - stradis 4:2:2 mpeg decoder driver + * + * Stradis 4:2:2 MPEG-2 Decoder Driver + * Copyright (C) 1999 Nathan Laredo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "saa7146.h" +#include "saa7146reg.h" +#include "ibmmpeg2.h" +#include "saa7121.h" +#include "cs8420.h" + +#define DEBUG(x) /* debug driver */ +#undef IDEBUG /* debug irq handler */ +#undef MDEBUG /* debug memory management */ + +#define SAA7146_MAX 6 + +static struct saa7146 saa7146s[SAA7146_MAX]; + +static int saa_num; /* number of SAA7146s in use */ + +static int video_nr = -1; +module_param(video_nr, int, 0); +MODULE_LICENSE("GPL"); + +#define nDebNormal 0x00480000 +#define nDebNoInc 0x00480000 +#define nDebVideo 0xd0480000 +#define nDebAudio 0xd0400000 +#define nDebDMA 0x02c80000 + +#define oDebNormal 0x13c80000 +#define oDebNoInc 0x13c80000 +#define oDebVideo 0xd1080000 +#define oDebAudio 0xd1080000 +#define oDebDMA 0x03080000 + +#define NewCard (saa->boardcfg[3]) +#define ChipControl (saa->boardcfg[1]) +#define NTSCFirstActive (saa->boardcfg[4]) +#define PALFirstActive (saa->boardcfg[5]) +#define NTSCLastActive (saa->boardcfg[54]) +#define PALLastActive (saa->boardcfg[55]) +#define Have2MB (saa->boardcfg[18] & 0x40) +#define HaveCS8420 (saa->boardcfg[18] & 0x04) +#define IBMMPEGCD20 (saa->boardcfg[18] & 0x20) +#define HaveCS3310 (saa->boardcfg[18] & 0x01) +#define CS3310MaxLvl ((saa->boardcfg[30] << 8) | saa->boardcfg[31]) +#define HaveCS4341 (saa->boardcfg[40] == 2) +#define SDIType (saa->boardcfg[27]) +#define CurrentMode (saa->boardcfg[2]) + +#define debNormal (NewCard ? nDebNormal : oDebNormal) +#define debNoInc (NewCard ? nDebNoInc : oDebNoInc) +#define debVideo (NewCard ? nDebVideo : oDebVideo) +#define debAudio (NewCard ? nDebAudio : oDebAudio) +#define debDMA (NewCard ? nDebDMA : oDebDMA) + +#ifdef USE_RESCUE_EEPROM_SDM275 +static unsigned char rescue_eeprom[64] = { + 0x00, 0x01, 0x04, 0x13, 0x26, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x43, 0x63, + 0x22, 0x01, 0x29, 0x15, 0x73, 0x00, 0x1f, 'd', 'e', 'c', 'x', 'l', + 'd', 'v', 'a', 0x02, 0x00, 0x01, 0x00, 0xcc, 0xa4, 0x63, 0x09, 0xe2, + 0x10, 0x00, 0x0a, 0x00, 0x02, 0x02, 'd', 'e', 'c', 'x', 'l', 'a', + 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; +#endif + +/* ----------------------------------------------------------------------- */ +/* Hardware I2C functions */ +static void I2CWipe(struct saa7146 *saa) +{ + int i; + /* set i2c to ~=100kHz, abort transfer, clear busy */ + saawrite(0x600 | SAA7146_I2C_ABORT, SAA7146_I2C_STATUS); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); + saawrite(0x600, SAA7146_I2C_STATUS); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); + saawrite(0x600, SAA7146_I2C_STATUS); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); +} + +/* read I2C */ +static int I2CRead(struct saa7146 *saa, unsigned char addr, + unsigned char subaddr, int dosub) +{ + int i; + + if (saaread(SAA7146_I2C_STATUS) & 0x3c) + I2CWipe(saa); + for (i = 0; + i < 1000 && (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); + i++) + schedule(); + if (i == 1000) + I2CWipe(saa); + if (dosub) + saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 8) | + ((subaddr & 0xff) << 16) | 0xed, SAA7146_I2C_TRANSFER); + else + saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 16) | + 0xf1, SAA7146_I2C_TRANSFER); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); + /* wait for valid data */ + for (i = 0; i < 1000 && + (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++) + schedule(); + if (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_ERR) + return -1; + if (i == 1000) + printk("i2c setup read timeout\n"); + saawrite(0x41, SAA7146_I2C_TRANSFER); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); + /* wait for valid data */ + for (i = 0; i < 1000 && + (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_BUSY); i++) + schedule(); + if (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_ERR) + return -1; + if (i == 1000) + printk("i2c read timeout\n"); + return ((saaread(SAA7146_I2C_TRANSFER) >> 24) & 0xff); +} + +/* set both to write both bytes, reset it to write only b1 */ + +static int I2CWrite(struct saa7146 *saa, unsigned char addr, unsigned char b1, + unsigned char b2, int both) +{ + int i; + u32 data; + + if (saaread(SAA7146_I2C_STATUS) & 0x3c) + I2CWipe(saa); + for (i = 0; i < 1000 && + (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++) + schedule(); + if (i == 1000) + I2CWipe(saa); + data = ((addr & 0xfe) << 24) | ((b1 & 0xff) << 16); + if (both) + data |= ((b2 & 0xff) << 8) | 0xe5; + else + data |= 0xd1; + saawrite(data, SAA7146_I2C_TRANSFER); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | SAA7146_MC2_UPLD_I2C, + SAA7146_MC2); + return 0; +} + +static void attach_inform(struct saa7146 *saa, int id) +{ + int i; + + DEBUG(printk(KERN_DEBUG "stradis%d: i2c: device found=%02x\n", saa->nr, + id)); + if (id == 0xa0) { /* we have rev2 or later board, fill in info */ + for (i = 0; i < 64; i++) + saa->boardcfg[i] = I2CRead(saa, 0xa0, i, 1); +#ifdef USE_RESCUE_EEPROM_SDM275 + if (saa->boardcfg[0] != 0) { + printk("stradis%d: WARNING: EEPROM STORED VALUES HAVE " + "BEEN IGNORED\n", saa->nr); + for (i = 0; i < 64; i++) + saa->boardcfg[i] = rescue_eeprom[i]; + } +#endif + printk("stradis%d: config =", saa->nr); + for (i = 0; i < 51; i++) { + printk(" %02x", saa->boardcfg[i]); + } + printk("\n"); + } +} + +static void I2CBusScan(struct saa7146 *saa) +{ + int i; + for (i = 0; i < 0xff; i += 2) + if ((I2CRead(saa, i, 0, 0)) >= 0) + attach_inform(saa, i); +} + +static int debiwait_maxwait; + +static int wait_for_debi_done(struct saa7146 *saa) +{ + int i; + + /* wait for registers to be programmed */ + for (i = 0; i < 100000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_DEBI); i++) + saaread(SAA7146_MC2); + /* wait for transfer to complete */ + for (i = 0; i < 500000 && + (saaread(SAA7146_PSR) & SAA7146_PSR_DEBI_S); i++) + saaread(SAA7146_MC2); + + if (i > debiwait_maxwait) + printk("wait-for-debi-done maxwait: %d\n", + debiwait_maxwait = i); + + if (i == 500000) + return -1; + + return 0; +} + +static int debiwrite(struct saa7146 *saa, u32 config, int addr, + u32 val, int count) +{ + u32 cmd; + if (count <= 0 || count > 32764) + return -1; + if (wait_for_debi_done(saa) < 0) + return -1; + saawrite(config, SAA7146_DEBI_CONFIG); + if (count <= 4) /* immediate transfer */ + saawrite(val, SAA7146_DEBI_AD); + else /* block transfer */ + saawrite(virt_to_bus(saa->dmadebi), SAA7146_DEBI_AD); + saawrite((cmd = (count << 17) | (addr & 0xffff)), SAA7146_DEBI_COMMAND); + saawrite((SAA7146_MC2_UPLD_DEBI << 16) | SAA7146_MC2_UPLD_DEBI, + SAA7146_MC2); + return 0; +} + +static u32 debiread(struct saa7146 *saa, u32 config, int addr, int count) +{ + u32 result = 0; + + if (count > 32764 || count <= 0) + return 0; + if (wait_for_debi_done(saa) < 0) + return 0; + saawrite(virt_to_bus(saa->dmadebi), SAA7146_DEBI_AD); + saawrite((count << 17) | 0x10000 | (addr & 0xffff), + SAA7146_DEBI_COMMAND); + saawrite(config, SAA7146_DEBI_CONFIG); + saawrite((SAA7146_MC2_UPLD_DEBI << 16) | SAA7146_MC2_UPLD_DEBI, + SAA7146_MC2); + if (count > 4) /* not an immediate transfer */ + return count; + wait_for_debi_done(saa); + result = saaread(SAA7146_DEBI_AD); + if (count == 1) + result &= 0xff; + if (count == 2) + result &= 0xffff; + if (count == 3) + result &= 0xffffff; + return result; +} + +static void do_irq_send_data(struct saa7146 *saa) +{ + int split, audbytes, vidbytes; + + saawrite(SAA7146_PSR_PIN1, SAA7146_IER); + /* if special feature mode in effect, disable audio sending */ + if (saa->playmode != VID_PLAY_NORMAL) + saa->audtail = saa->audhead = 0; + if (saa->audhead <= saa->audtail) + audbytes = saa->audtail - saa->audhead; + else + audbytes = 65536 - (saa->audhead - saa->audtail); + if (saa->vidhead <= saa->vidtail) + vidbytes = saa->vidtail - saa->vidhead; + else + vidbytes = 524288 - (saa->vidhead - saa->vidtail); + if (audbytes == 0 && vidbytes == 0 && saa->osdtail == saa->osdhead) { + saawrite(0, SAA7146_IER); + return; + } + /* if at least 1 block audio waiting and audio fifo isn't full */ + if (audbytes >= 2048 && (debiread(saa, debNormal, IBM_MP2_AUD_FIFO, 2) + & 0xff) < 60) { + if (saa->audhead > saa->audtail) + split = 65536 - saa->audhead; + else + split = 0; + audbytes = 2048; + if (split > 0 && split < 2048) { + memcpy(saa->dmadebi, saa->audbuf + saa->audhead, split); + saa->audhead = 0; + audbytes -= split; + } else + split = 0; + memcpy(saa->dmadebi + split, saa->audbuf + saa->audhead, + audbytes); + saa->audhead += audbytes; + saa->audhead &= 0xffff; + debiwrite(saa, debAudio, (NewCard ? IBM_MP2_AUD_FIFO : + IBM_MP2_AUD_FIFOW), 0, 2048); + wake_up_interruptible(&saa->audq); + /* if at least 1 block video waiting and video fifo isn't full */ + } else if (vidbytes >= 30720 && (debiread(saa, debNormal, + IBM_MP2_FIFO, 2)) < 16384) { + if (saa->vidhead > saa->vidtail) + split = 524288 - saa->vidhead; + else + split = 0; + vidbytes = 30720; + if (split > 0 && split < 30720) { + memcpy(saa->dmadebi, saa->vidbuf + saa->vidhead, split); + saa->vidhead = 0; + vidbytes -= split; + } else + split = 0; + memcpy(saa->dmadebi + split, saa->vidbuf + saa->vidhead, + vidbytes); + saa->vidhead += vidbytes; + saa->vidhead &= 0x7ffff; + debiwrite(saa, debVideo, (NewCard ? IBM_MP2_FIFO : + IBM_MP2_FIFOW), 0, 30720); + wake_up_interruptible(&saa->vidq); + } + saawrite(SAA7146_PSR_DEBI_S | SAA7146_PSR_PIN1, SAA7146_IER); +} + +static void send_osd_data(struct saa7146 *saa) +{ + int size = saa->osdtail - saa->osdhead; + if (size > 30720) + size = 30720; + /* ensure some multiple of 8 bytes is transferred */ + size = 8 * ((size + 8) >> 3); + if (size) { + debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR, + (saa->osdhead >> 3), 2); + memcpy(saa->dmadebi, &saa->osdbuf[saa->osdhead], size); + saa->osdhead += size; + /* block transfer of next 8 bytes to ~32k bytes */ + debiwrite(saa, debNormal, IBM_MP2_OSD_DATA, 0, size); + } + if (saa->osdhead >= saa->osdtail) { + saa->osdhead = saa->osdtail = 0; + debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); + } +} + +static irqreturn_t saa7146_irq(int irq, void *dev_id) +{ + struct saa7146 *saa = dev_id; + u32 stat, astat; + int count; + int handled = 0; + + count = 0; + while (1) { + /* get/clear interrupt status bits */ + stat = saaread(SAA7146_ISR); + astat = stat & saaread(SAA7146_IER); + if (!astat) + break; + handled = 1; + saawrite(astat, SAA7146_ISR); + if (astat & SAA7146_PSR_DEBI_S) { + do_irq_send_data(saa); + } + if (astat & SAA7146_PSR_PIN1) { + int istat; + /* the following read will trigger DEBI_S */ + istat = debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); + if (istat & 1) { + saawrite(0, SAA7146_IER); + send_osd_data(saa); + saawrite(SAA7146_PSR_DEBI_S | + SAA7146_PSR_PIN1, SAA7146_IER); + } + if (istat & 0x20) { /* Video Start */ + saa->vidinfo.frame_count++; + } + if (istat & 0x400) { /* Picture Start */ + /* update temporal reference */ + } + if (istat & 0x200) { /* Picture Resolution Change */ + /* read new resolution */ + } + if (istat & 0x100) { /* New User Data found */ + /* read new user data */ + } + if (istat & 0x1000) { /* new GOP/SMPTE */ + /* read new SMPTE */ + } + if (istat & 0x8000) { /* Sequence Start Code */ + /* reset frame counter, load sizes */ + saa->vidinfo.frame_count = 0; + saa->vidinfo.h_size = 704; + saa->vidinfo.v_size = 480; +#if 0 + if (saa->endmarkhead != saa->endmarktail) { + saa->audhead = + saa->endmark[saa->endmarkhead]; + saa->endmarkhead++; + if (saa->endmarkhead >= MAX_MARKS) + saa->endmarkhead = 0; + } +#endif + } + if (istat & 0x4000) { /* Sequence Error Code */ + if (saa->endmarkhead != saa->endmarktail) { + saa->audhead = + saa->endmark[saa->endmarkhead]; + saa->endmarkhead++; + if (saa->endmarkhead >= MAX_MARKS) + saa->endmarkhead = 0; + } + } + } +#ifdef IDEBUG + if (astat & SAA7146_PSR_PPEF) { + IDEBUG(printk("stradis%d irq: PPEF\n", saa->nr)); + } + if (astat & SAA7146_PSR_PABO) { + IDEBUG(printk("stradis%d irq: PABO\n", saa->nr)); + } + if (astat & SAA7146_PSR_PPED) { + IDEBUG(printk("stradis%d irq: PPED\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_I1) { + IDEBUG(printk("stradis%d irq: RPS_I1\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_I0) { + IDEBUG(printk("stradis%d irq: RPS_I0\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_LATE1) { + IDEBUG(printk("stradis%d irq: RPS_LATE1\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_LATE0) { + IDEBUG(printk("stradis%d irq: RPS_LATE0\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_E1) { + IDEBUG(printk("stradis%d irq: RPS_E1\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_E0) { + IDEBUG(printk("stradis%d irq: RPS_E0\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_TO1) { + IDEBUG(printk("stradis%d irq: RPS_TO1\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_TO0) { + IDEBUG(printk("stradis%d irq: RPS_TO0\n", saa->nr)); + } + if (astat & SAA7146_PSR_UPLD) { + IDEBUG(printk("stradis%d irq: UPLD\n", saa->nr)); + } + if (astat & SAA7146_PSR_DEBI_E) { + IDEBUG(printk("stradis%d irq: DEBI_E\n", saa->nr)); + } + if (astat & SAA7146_PSR_I2C_S) { + IDEBUG(printk("stradis%d irq: I2C_S\n", saa->nr)); + } + if (astat & SAA7146_PSR_I2C_E) { + IDEBUG(printk("stradis%d irq: I2C_E\n", saa->nr)); + } + if (astat & SAA7146_PSR_A2_IN) { + IDEBUG(printk("stradis%d irq: A2_IN\n", saa->nr)); + } + if (astat & SAA7146_PSR_A2_OUT) { + IDEBUG(printk("stradis%d irq: A2_OUT\n", saa->nr)); + } + if (astat & SAA7146_PSR_A1_IN) { + IDEBUG(printk("stradis%d irq: A1_IN\n", saa->nr)); + } + if (astat & SAA7146_PSR_A1_OUT) { + IDEBUG(printk("stradis%d irq: A1_OUT\n", saa->nr)); + } + if (astat & SAA7146_PSR_AFOU) { + IDEBUG(printk("stradis%d irq: AFOU\n", saa->nr)); + } + if (astat & SAA7146_PSR_V_PE) { + IDEBUG(printk("stradis%d irq: V_PE\n", saa->nr)); + } + if (astat & SAA7146_PSR_VFOU) { + IDEBUG(printk("stradis%d irq: VFOU\n", saa->nr)); + } + if (astat & SAA7146_PSR_FIDA) { + IDEBUG(printk("stradis%d irq: FIDA\n", saa->nr)); + } + if (astat & SAA7146_PSR_FIDB) { + IDEBUG(printk("stradis%d irq: FIDB\n", saa->nr)); + } + if (astat & SAA7146_PSR_PIN3) { + IDEBUG(printk("stradis%d irq: PIN3\n", saa->nr)); + } + if (astat & SAA7146_PSR_PIN2) { + IDEBUG(printk("stradis%d irq: PIN2\n", saa->nr)); + } + if (astat & SAA7146_PSR_PIN0) { + IDEBUG(printk("stradis%d irq: PIN0\n", saa->nr)); + } + if (astat & SAA7146_PSR_ECS) { + IDEBUG(printk("stradis%d irq: ECS\n", saa->nr)); + } + if (astat & SAA7146_PSR_EC3S) { + IDEBUG(printk("stradis%d irq: EC3S\n", saa->nr)); + } + if (astat & SAA7146_PSR_EC0S) { + IDEBUG(printk("stradis%d irq: EC0S\n", saa->nr)); + } +#endif + count++; + if (count > 15) + printk(KERN_WARNING "stradis%d: irq loop %d\n", + saa->nr, count); + if (count > 20) { + saawrite(0, SAA7146_IER); + printk(KERN_ERR + "stradis%d: IRQ loop cleared\n", saa->nr); + } + } + return IRQ_RETVAL(handled); +} + +static int ibm_send_command(struct saa7146 *saa, + int command, int data, int chain) +{ + int i; + + if (chain) + debiwrite(saa, debNormal, IBM_MP2_COMMAND, (command << 1)| 1,2); + else + debiwrite(saa, debNormal, IBM_MP2_COMMAND, command << 1, 2); + debiwrite(saa, debNormal, IBM_MP2_CMD_DATA, data, 2); + debiwrite(saa, debNormal, IBM_MP2_CMD_STAT, 1, 2); + for (i = 0; i < 100 && + (debiread(saa, debNormal, IBM_MP2_CMD_STAT, 2) & 1); i++) + schedule(); + if (i == 100) + return -1; + return 0; +} + +static void cs4341_setlevel(struct saa7146 *saa, int left, int right) +{ + I2CWrite(saa, 0x22, 0x03, left > 94 ? 94 : left, 2); + I2CWrite(saa, 0x22, 0x04, right > 94 ? 94 : right, 2); +} + +static void initialize_cs4341(struct saa7146 *saa) +{ + int i; + for (i = 0; i < 200; i++) { + /* auto mute off, power on, no de-emphasis */ + /* I2S data up to 24-bit 64xFs internal SCLK */ + I2CWrite(saa, 0x22, 0x01, 0x11, 2); + /* ATAPI mixer settings */ + I2CWrite(saa, 0x22, 0x02, 0x49, 2); + /* attenuation left 3db */ + I2CWrite(saa, 0x22, 0x03, 0x00, 2); + /* attenuation right 3db */ + I2CWrite(saa, 0x22, 0x04, 0x00, 2); + I2CWrite(saa, 0x22, 0x01, 0x10, 2); + if (I2CRead(saa, 0x22, 0x02, 1) == 0x49) + break; + schedule(); + } + printk("stradis%d: CS4341 initialized (%d)\n", saa->nr, i); + return; +} + +static void initialize_cs8420(struct saa7146 *saa, int pro) +{ + int i; + u8 *sequence; + if (pro) + sequence = mode8420pro; + else + sequence = mode8420con; + for (i = 0; i < INIT8420LEN; i++) + I2CWrite(saa, 0x20, init8420[i * 2], init8420[i * 2 + 1], 2); + for (i = 0; i < MODE8420LEN; i++) + I2CWrite(saa, 0x20, sequence[i * 2], sequence[i * 2 + 1], 2); + printk("stradis%d: CS8420 initialized\n", saa->nr); +} + +static void initialize_saa7121(struct saa7146 *saa, int dopal) +{ + int i, mod; + u8 *sequence; + if (dopal) + sequence = init7121pal; + else + sequence = init7121ntsc; + mod = saaread(SAA7146_PSR) & 0x08; + /* initialize PAL/NTSC video encoder */ + for (i = 0; i < INIT7121LEN; i++) { + if (NewCard) { /* handle new card encoder differences */ + if (sequence[i * 2] == 0x3a) + I2CWrite(saa, 0x88, 0x3a, 0x13, 2); + else if (sequence[i * 2] == 0x6b) + I2CWrite(saa, 0x88, 0x6b, 0x20, 2); + else if (sequence[i * 2] == 0x6c) + I2CWrite(saa, 0x88, 0x6c, + dopal ? 0x09 : 0xf5, 2); + else if (sequence[i * 2] == 0x6d) + I2CWrite(saa, 0x88, 0x6d, + dopal ? 0x20 : 0x00, 2); + else if (sequence[i * 2] == 0x7a) + I2CWrite(saa, 0x88, 0x7a, + dopal ? (PALFirstActive - 1) : + (NTSCFirstActive - 4), 2); + else if (sequence[i * 2] == 0x7b) + I2CWrite(saa, 0x88, 0x7b, + dopal ? PALLastActive : + NTSCLastActive, 2); + else + I2CWrite(saa, 0x88, sequence[i * 2], + sequence[i * 2 + 1], 2); + } else { + if (sequence[i * 2] == 0x6b && mod) + I2CWrite(saa, 0x88, 0x6b, + (sequence[i * 2 + 1] ^ 0x09), 2); + else if (sequence[i * 2] == 0x7a) + I2CWrite(saa, 0x88, 0x7a, + dopal ? (PALFirstActive - 1) : + (NTSCFirstActive - 4), 2); + else if (sequence[i * 2] == 0x7b) + I2CWrite(saa, 0x88, 0x7b, + dopal ? PALLastActive : + NTSCLastActive, 2); + else + I2CWrite(saa, 0x88, sequence[i * 2], + sequence[i * 2 + 1], 2); + } + } +} + +static void set_genlock_offset(struct saa7146 *saa, int noffset) +{ + int nCode; + int PixelsPerLine = 858; + if (CurrentMode == VIDEO_MODE_PAL) + PixelsPerLine = 864; + if (noffset > 500) + noffset = 500; + else if (noffset < -500) + noffset = -500; + nCode = noffset + 0x100; + if (nCode == 1) + nCode = 0x401; + else if (nCode < 1) + nCode = 0x400 + PixelsPerLine + nCode; + debiwrite(saa, debNormal, XILINX_GLDELAY, nCode, 2); +} + +static void set_out_format(struct saa7146 *saa, int mode) +{ + initialize_saa7121(saa, (mode == VIDEO_MODE_NTSC ? 0 : 1)); + saa->boardcfg[2] = mode; + /* do not adjust analog video parameters here, use saa7121 init */ + /* you will affect the SDI output on the new card */ + if (mode == VIDEO_MODE_PAL) { /* PAL */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x0808, 2); + mdelay(50); + saawrite(0x012002c0, SAA7146_NUM_LINE_BYTE1); + if (NewCard) { + debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, 0xe100, 2); + mdelay(50); + } + debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, + NewCard ? 0xe500 : 0x6500, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_DLY, + (1 << 8) | + (NewCard ? PALFirstActive : PALFirstActive - 6), 2); + } else { /* NTSC */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x0800, 2); + mdelay(50); + saawrite(0x00f002c0, SAA7146_NUM_LINE_BYTE1); + debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, + NewCard ? 0xe100 : 0x6100, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_DLY, + (1 << 8) | + (NewCard ? NTSCFirstActive : NTSCFirstActive - 6), 2); + } +} + +/* Intialize bitmangler to map from a byte value to the mangled word that + * must be output to program the Xilinx part through the DEBI port. + * Xilinx Data Bit->DEBI Bit: 0->15 1->7 2->6 3->12 4->11 5->2 6->1 7->0 + * transfer FPGA code, init IBM chip, transfer IBM microcode + * rev2 card mangles: 0->7 1->6 2->5 3->4 4->3 5->2 6->1 7->0 + */ +static u16 bitmangler[256]; + +static int initialize_fpga(struct video_code *bitdata) +{ + int i, num, startindex, failure = 0, loadtwo, loadfile = 0; + u16 *dmabuf; + u8 *newdma; + struct saa7146 *saa; + + /* verify fpga code */ + for (startindex = 0; startindex < bitdata->datasize; startindex++) + if (bitdata->data[startindex] == 255) + break; + if (startindex == bitdata->datasize) { + printk(KERN_INFO "stradis: bad fpga code\n"); + return -1; + } + /* initialize all detected cards */ + for (num = 0; num < saa_num; num++) { + saa = &saa7146s[num]; + if (saa->boardcfg[0] > 20) + continue; /* card was programmed */ + loadtwo = (saa->boardcfg[18] & 0x10); + if (!NewCard) /* we have an old board */ + for (i = 0; i < 256; i++) + bitmangler[i] = ((i & 0x01) << 15) | + ((i & 0x02) << 6) | ((i & 0x04) << 4) | + ((i & 0x08) << 9) | ((i & 0x10) << 7) | + ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | + ((i & 0x80) >> 7); + else /* else we have a new board */ + for (i = 0; i < 256; i++) + bitmangler[i] = ((i & 0x01) << 7) | + ((i & 0x02) << 5) | ((i & 0x04) << 3) | + ((i & 0x08) << 1) | ((i & 0x10) >> 1) | + ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | + ((i & 0x80) >> 7); + + dmabuf = (u16 *) saa->dmadebi; + newdma = (u8 *) saa->dmadebi; + if (NewCard) { /* SDM2xxx */ + if (!strncmp(bitdata->loadwhat, "decoder2", 8)) + continue; /* fpga not for this card */ + if (!strncmp(&saa->boardcfg[42], bitdata->loadwhat, 8)) + loadfile = 1; + else if (loadtwo && !strncmp(&saa->boardcfg[19], + bitdata->loadwhat, 8)) + loadfile = 2; + else if (!saa->boardcfg[42] && !strncmp("decxl", + bitdata->loadwhat, 8)) + loadfile = 1; /* special */ + else + continue; /* fpga not for this card */ + if (loadfile != 1 && loadfile != 2) + continue; /* skip to next card */ + if (saa->boardcfg[0] && loadfile == 1) + continue; /* skip to next card */ + if (saa->boardcfg[0] != 1 && loadfile == 2) + continue; /* skip to next card */ + saa->boardcfg[0]++; /* mark fpga handled */ + printk("stradis%d: loading %s\n", saa->nr, + bitdata->loadwhat); + if (loadtwo && loadfile == 2) + goto send_fpga_stuff; + /* turn on the Audio interface to set PROG low */ + saawrite(0x00400040, SAA7146_GPIO_CTRL); + saaread(SAA7146_PSR); /* ensure posted write */ + /* wait for everyone to reset */ + mdelay(10); + saawrite(0x00400000, SAA7146_GPIO_CTRL); + } else { /* original card */ + if (strncmp(bitdata->loadwhat, "decoder2", 8)) + continue; /* fpga not for this card */ + /* Pull the Xilinx PROG signal WS3 low */ + saawrite(0x02000200, SAA7146_MC1); + /* Turn on the Audio interface so can set PROG low */ + saawrite(0x000000c0, SAA7146_ACON1); + /* Pull the Xilinx INIT signal (GPIO2) low */ + saawrite(0x00400000, SAA7146_GPIO_CTRL); + /* Make sure everybody resets */ + saaread(SAA7146_PSR); /* ensure posted write */ + mdelay(10); + /* Release the Xilinx PROG signal */ + saawrite(0x00000000, SAA7146_ACON1); + /* Turn off the Audio interface */ + saawrite(0x02000000, SAA7146_MC1); + } + /* Release Xilinx INIT signal (WS2) */ + saawrite(0x00000000, SAA7146_GPIO_CTRL); + /* Wait for the INIT to go High */ + for (i = 0; + i < 10000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); + i++) + schedule(); + if (i == 1000) { + printk(KERN_INFO "stradis%d: no fpga INIT\n", saa->nr); + return -1; + } +send_fpga_stuff: + if (NewCard) { + for (i = startindex; i < bitdata->datasize; i++) + newdma[i - startindex] = + bitmangler[bitdata->data[i]]; + debiwrite(saa, 0x01420000, 0, 0, + ((bitdata->datasize - startindex) + 5)); + if (loadtwo && loadfile == 1) { + printk("stradis%d: awaiting 2nd FPGA bitfile\n", + saa->nr); + continue; /* skip to next card */ + } + } else { + for (i = startindex; i < bitdata->datasize; i++) + dmabuf[i - startindex] = + bitmangler[bitdata->data[i]]; + debiwrite(saa, 0x014a0000, 0, 0, + ((bitdata->datasize - startindex) + 5) * 2); + } + for (i = 0; + i < 1000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); + i++) + schedule(); + if (i == 1000) { + printk(KERN_INFO "stradis%d: FPGA load failed\n", + saa->nr); + failure++; + continue; + } + if (!NewCard) { + /* Pull the Xilinx INIT signal (GPIO2) low */ + saawrite(0x00400000, SAA7146_GPIO_CTRL); + saaread(SAA7146_PSR); /* ensure posted write */ + mdelay(2); + saawrite(0x00000000, SAA7146_GPIO_CTRL); + mdelay(2); + } + printk(KERN_INFO "stradis%d: FPGA Loaded\n", saa->nr); + saa->boardcfg[0] = 26; /* mark fpga programmed */ + /* set VXCO to its lowest frequency */ + debiwrite(saa, debNormal, XILINX_PWM, 0, 2); + if (NewCard) { + /* mute CS3310 */ + if (HaveCS3310) + debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, + 0, 2); + /* set VXCO to PWM mode, release reset, blank on */ + debiwrite(saa, debNormal, XILINX_CTL0, 0xffc4, 2); + mdelay(10); + /* unmute CS3310 */ + if (HaveCS3310) + debiwrite(saa, debNormal, XILINX_CTL0, + 0x2020, 2); + } + /* set source Black */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2); + saa->boardcfg[4] = 22; /* set NTSC First Active Line */ + saa->boardcfg[5] = 23; /* set PAL First Active Line */ + saa->boardcfg[54] = 2; /* set NTSC Last Active Line - 256 */ + saa->boardcfg[55] = 54; /* set PAL Last Active Line - 256 */ + set_out_format(saa, VIDEO_MODE_NTSC); + mdelay(50); + /* begin IBM chip init */ + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 4, 2); + saaread(SAA7146_PSR); /* wait for reset */ + mdelay(5); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0, 2); + debiread(saa, debNormal, IBM_MP2_CHIP_CONTROL, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0x10, 2); + debiwrite(saa, debNormal, IBM_MP2_CMD_ADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_MODE, 0x2e, 2); + if (NewCard) { + mdelay(5); + /* set i2s rate converter to 48KHz */ + debiwrite(saa, debNormal, 0x80c0, 6, 2); + /* we must init CS8420 first since rev b pulls i2s */ + /* master clock low and CS4341 needs i2s master to */ + /* run the i2c port. */ + if (HaveCS8420) + /* 0=consumer, 1=pro */ + initialize_cs8420(saa, 0); + + mdelay(5); + if (HaveCS4341) + initialize_cs4341(saa); + } + debiwrite(saa, debNormal, IBM_MP2_INFC_CTL, 0x48, 2); + debiwrite(saa, debNormal, IBM_MP2_BEEP_CTL, 0xa000, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_LBOR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_TBOR, 0, 2); + if (NewCard) + set_genlock_offset(saa, 0); + debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2); +#if 0 + /* enable genlock */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x8000, 2); +#else + /* disable genlock */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x8080, 2); +#endif + } + + return failure; +} + +static int do_ibm_reset(struct saa7146 *saa) +{ + /* failure if decoder not previously programmed */ + if (saa->boardcfg[0] < 37) + return -EIO; + /* mute CS3310 */ + if (HaveCS3310) + debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, 0, 2); + /* disable interrupts */ + saawrite(0, SAA7146_IER); + saa->audhead = saa->audtail = 0; + saa->vidhead = saa->vidtail = 0; + /* tristate debi bus, disable debi transfers */ + saawrite(0x00880000, SAA7146_MC1); + /* ensure posted write */ + saaread(SAA7146_MC1); + mdelay(50); + /* re-enable debi transfers */ + saawrite(0x00880088, SAA7146_MC1); + /* set source Black */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2); + /* begin IBM chip init */ + set_out_format(saa, CurrentMode); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 4, 2); + saaread(SAA7146_PSR); /* wait for reset */ + mdelay(5); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0, 2); + debiread(saa, debNormal, IBM_MP2_CHIP_CONTROL, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_MODE, 0x2e, 2); + if (NewCard) { + mdelay(5); + /* set i2s rate converter to 48KHz */ + debiwrite(saa, debNormal, 0x80c0, 6, 2); + /* we must init CS8420 first since rev b pulls i2s */ + /* master clock low and CS4341 needs i2s master to */ + /* run the i2c port. */ + if (HaveCS8420) + /* 0=consumer, 1=pro */ + initialize_cs8420(saa, 1); + + mdelay(5); + if (HaveCS4341) + initialize_cs4341(saa); + } + debiwrite(saa, debNormal, IBM_MP2_INFC_CTL, 0x48, 2); + debiwrite(saa, debNormal, IBM_MP2_BEEP_CTL, 0xa000, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_LBOR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_TBOR, 0, 2); + if (NewCard) + set_genlock_offset(saa, 0); + debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2); + debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2); + if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER, + (ChipControl == 0x43 ? 0xe800 : 0xe000), 1)) { + printk(KERN_ERR "stradis%d: IBM config failed\n", saa->nr); + } + if (HaveCS3310) { + int i = CS3310MaxLvl; + debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, ((i << 8)| i),2); + } + /* start video decoder */ + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); + /* 256k vid, 3520 bytes aud */ + debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037, 2); + debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2); + ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); + /* enable buffer threshold irq */ + debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); + /* clear pending interrupts */ + debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); + debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2); + + return 0; +} + +/* load the decoder microcode */ +static int initialize_ibmmpeg2(struct video_code *microcode) +{ + int i, num; + struct saa7146 *saa; + + for (num = 0; num < saa_num; num++) { + saa = &saa7146s[num]; + /* check that FPGA is loaded */ + debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0xa55a, 2); + i = debiread(saa, debNormal, IBM_MP2_OSD_SIZE, 2); + if (i != 0xa55a) { + printk(KERN_INFO "stradis%d: %04x != 0xa55a\n", + saa->nr, i); +#if 0 + return -1; +#endif + } + if (!strncmp(microcode->loadwhat, "decoder.vid", 11)) { + if (saa->boardcfg[0] > 27) + continue; /* skip to next card */ + /* load video control store */ + saa->boardcfg[1] = 0x13; /* no-sync default */ + debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2); + debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2); + for (i = 0; i < microcode->datasize / 2; i++) + debiwrite(saa, debNormal, IBM_MP2_PROC_IDATA, + (microcode->data[i * 2] << 8) | + microcode->data[i * 2 + 1], 2); + debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, + ChipControl, 2); + saa->boardcfg[0] = 28; + } + if (!strncmp(microcode->loadwhat, "decoder.aud", 11)) { + if (saa->boardcfg[0] > 35) + continue; /* skip to next card */ + /* load audio control store */ + debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2); + debiwrite(saa, debNormal, IBM_MP2_AUD_IADDR, 0, 2); + for (i = 0; i < microcode->datasize; i++) + debiwrite(saa, debNormal, IBM_MP2_AUD_IDATA, + microcode->data[i], 1); + debiwrite(saa, debNormal, IBM_MP2_AUD_IADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2); + debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2); + if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER, + 0xe000, 1)) { + printk(KERN_ERR "stradis%d: IBM config " + "failed\n", saa->nr); + return -1; + } + /* set PWM to center value */ + if (NewCard) { + debiwrite(saa, debNormal, XILINX_PWM, + saa->boardcfg[14] + + (saa->boardcfg[13] << 8), 2); + } else + debiwrite(saa, debNormal, XILINX_PWM, 0x46, 2); + + if (HaveCS3310) { + i = CS3310MaxLvl; + debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, + (i << 8) | i, 2); + } + printk(KERN_INFO "stradis%d: IBM MPEGCD%d Inited\n", + saa->nr, 18 + (debiread(saa, debNormal, + IBM_MP2_CHIP_CONTROL, 2) >> 12)); + /* start video decoder */ + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, + ChipControl, 2); + debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037, + 2); /* 256k vid, 3520 bytes aud */ + debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2); + ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); + /* enable buffer threshold irq */ + debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); + debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); + /* enable gpio irq */ + saawrite(0x00002000, SAA7146_GPIO_CTRL); + /* enable decoder output to HPS */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2); + saa->boardcfg[0] = 37; + } + } + + return 0; +} + +static u32 palette2fmt[] = { /* some of these YUV translations are wrong */ + 0xffffffff, 0x86000000, 0x87000000, 0x80000000, 0x8100000, 0x82000000, + 0x83000000, 0x00000000, 0x03000000, 0x03000000, 0x0a00000, 0x03000000, + 0x06000000, 0x00000000, 0x03000000, 0x0a000000, 0x0300000 +}; +static int bpp2fmt[4] = { + VIDEO_PALETTE_HI240, VIDEO_PALETTE_RGB565, VIDEO_PALETTE_RGB24, + VIDEO_PALETTE_RGB32 +}; + +/* I wish I could find a formula to calculate these... */ +static u32 h_prescale[64] = { + 0x10000000, 0x18040202, 0x18080000, 0x380c0606, 0x38100204, 0x38140808, + 0x38180000, 0x381c0000, 0x3820161c, 0x38242a3b, 0x38281230, 0x382c4460, + 0x38301040, 0x38340080, 0x38380000, 0x383c0000, 0x3840fefe, 0x3844ee9f, + 0x3848ee9f, 0x384cee9f, 0x3850ee9f, 0x38542a3b, 0x38581230, 0x385c0000, + 0x38600000, 0x38640000, 0x38680000, 0x386c0000, 0x38700000, 0x38740000, + 0x38780000, 0x387c0000, 0x30800000, 0x38840000, 0x38880000, 0x388c0000, + 0x38900000, 0x38940000, 0x38980000, 0x389c0000, 0x38a00000, 0x38a40000, + 0x38a80000, 0x38ac0000, 0x38b00000, 0x38b40000, 0x38b80000, 0x38bc0000, + 0x38c00000, 0x38c40000, 0x38c80000, 0x38cc0000, 0x38d00000, 0x38d40000, + 0x38d80000, 0x38dc0000, 0x38e00000, 0x38e40000, 0x38e80000, 0x38ec0000, + 0x38f00000, 0x38f40000, 0x38f80000, 0x38fc0000, +}; +static u32 v_gain[64] = { + 0x016000ff, 0x016100ff, 0x016100ff, 0x016200ff, 0x016200ff, 0x016200ff, + 0x016200ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, + 0x016300ff, 0x016300ff, 0x016300ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, +}; + +static void saa7146_set_winsize(struct saa7146 *saa) +{ + u32 format; + int offset, yacl, ysci; + saa->win.color_fmt = format = + (saa->win.depth == 15) ? palette2fmt[VIDEO_PALETTE_RGB555] : + palette2fmt[bpp2fmt[(saa->win.bpp - 1) & 3]]; + offset = saa->win.x * saa->win.bpp + saa->win.y * saa->win.bpl; + saawrite(saa->win.vidadr + offset, SAA7146_BASE_EVEN1); + saawrite(saa->win.vidadr + offset + saa->win.bpl, SAA7146_BASE_ODD1); + saawrite(saa->win.bpl * 2, SAA7146_PITCH1); + saawrite(saa->win.vidadr + saa->win.bpl * saa->win.sheight, + SAA7146_PROT_ADDR1); + saawrite(0, SAA7146_PAGE1); + saawrite(format | 0x60, SAA7146_CLIP_FORMAT_CTRL); + offset = (704 / (saa->win.width - 1)) & 0x3f; + saawrite(h_prescale[offset], SAA7146_HPS_H_PRESCALE); + offset = (720896 / saa->win.width) / (offset + 1); + saawrite((offset << 12) | 0x0c, SAA7146_HPS_H_SCALE); + if (CurrentMode == VIDEO_MODE_NTSC) { + yacl = /*(480 / saa->win.height - 1) & 0x3f */ 0; + ysci = 1024 - (saa->win.height * 1024 / 480); + } else { + yacl = /*(576 / saa->win.height - 1) & 0x3f */ 0; + ysci = 1024 - (saa->win.height * 1024 / 576); + } + saawrite((1 << 31) | (ysci << 21) | (yacl << 15), SAA7146_HPS_V_SCALE); + saawrite(v_gain[yacl], SAA7146_HPS_V_GAIN); + saawrite(((SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_HPS_V | + SAA7146_MC2_UPLD_HPS_H) << 16) | (SAA7146_MC2_UPLD_DMA1 | + SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_HPS_H), SAA7146_MC2); +} + +/* clip_draw_rectangle(cm,x,y,w,h) -- handle clipping an area + * bitmap is fixed width, 128 bytes (1024 pixels represented) + * arranged most-sigificant-bit-left in 32-bit words + * based on saa7146 clipping hardware, it swaps bytes if LE + * much of this makes up for egcs brain damage -- so if you + * are wondering "why did he do this?" it is because the C + * was adjusted to generate the optimal asm output without + * writing non-portable __asm__ directives. + */ + +static void clip_draw_rectangle(u32 *clipmap, int x, int y, int w, int h) +{ + register int startword, endword; + register u32 bitsleft, bitsright; + u32 *temp; + if (x < 0) { + w += x; + x = 0; + } + if (y < 0) { + h += y; + y = 0; + } + if (w <= 0 || h <= 0 || x > 1023 || y > 639) + return; /* throw away bad clips */ + if (x + w > 1024) + w = 1024 - x; + if (y + h > 640) + h = 640 - y; + startword = (x >> 5); + endword = ((x + w) >> 5); + bitsleft = (0xffffffff >> (x & 31)); + bitsright = (0xffffffff << (~((x + w) - (endword << 5)))); + temp = &clipmap[(y << 5) + startword]; + w = endword - startword; + if (!w) { + bitsleft |= bitsright; + for (y = 0; y < h; y++) { + *temp |= bitsleft; + temp += 32; + } + } else { + for (y = 0; y < h; y++) { + *temp++ |= bitsleft; + for (x = 1; x < w; x++) + *temp++ = 0xffffffff; + *temp |= bitsright; + temp += (32 - w); + } + } +} + +static void make_clip_tab(struct saa7146 *saa, struct video_clip *cr, int ncr) +{ + int i, width, height; + u32 *clipmap; + + clipmap = saa->dmavid2; + if ((width = saa->win.width) > 1023) + width = 1023; /* sanity check */ + if ((height = saa->win.height) > 640) + height = 639; /* sanity check */ + if (ncr > 0) { /* rectangles pased */ + /* convert rectangular clips to a bitmap */ + memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */ + for (i = 0; i < ncr; i++) + clip_draw_rectangle(clipmap, cr[i].x, cr[i].y, + cr[i].width, cr[i].height); + } + /* clip against viewing window AND screen + so we do not have to rely on the user program + */ + clip_draw_rectangle(clipmap, (saa->win.x + width > saa->win.swidth) ? + (saa->win.swidth - saa->win.x) : width, 0, 1024, 768); + clip_draw_rectangle(clipmap, 0, + (saa->win.y + height > saa->win.sheight) ? + (saa->win.sheight - saa->win.y) : height, 1024, 768); + if (saa->win.x < 0) + clip_draw_rectangle(clipmap, 0, 0, -saa->win.x, 768); + if (saa->win.y < 0) + clip_draw_rectangle(clipmap, 0, 0, 1024, -saa->win.y); +} + +static long saa_ioctl(struct file *file, + unsigned int cmd, unsigned long argl) +{ + struct saa7146 *saa = file->private_data; + void __user *arg = (void __user *)argl; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability b; + strcpy(b.name, saa->video_dev.name); + b.type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | + VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM | + VID_TYPE_SCALES; + b.channels = 1; + b.audios = 1; + b.maxwidth = 768; + b.maxheight = 576; + b.minwidth = 32; + b.minheight = 32; + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture p = saa->picture; + if (saa->win.depth == 8) + p.palette = VIDEO_PALETTE_HI240; + if (saa->win.depth == 15) + p.palette = VIDEO_PALETTE_RGB555; + if (saa->win.depth == 16) + p.palette = VIDEO_PALETTE_RGB565; + if (saa->win.depth == 24) + p.palette = VIDEO_PALETTE_RGB24; + if (saa->win.depth == 32) + p.palette = VIDEO_PALETTE_RGB32; + if (copy_to_user(arg, &p, sizeof(p))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture p; + u32 format; + if (copy_from_user(&p, arg, sizeof(p))) + return -EFAULT; + if (p.palette < ARRAY_SIZE(palette2fmt)) { + format = palette2fmt[p.palette]; + saa->win.color_fmt = format; + saawrite(format | 0x60, + SAA7146_CLIP_FORMAT_CTRL); + } + saawrite(((p.brightness & 0xff00) << 16) | + ((p.contrast & 0xfe00) << 7) | + ((p.colour & 0xfe00) >> 9), SAA7146_BCS_CTRL); + saa->picture = p; + /* upload changed registers */ + saawrite(((SAA7146_MC2_UPLD_HPS_H | + SAA7146_MC2_UPLD_HPS_V) << 16) | + SAA7146_MC2_UPLD_HPS_H | + SAA7146_MC2_UPLD_HPS_V, SAA7146_MC2); + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + struct video_clip *vcp = NULL; + + if (copy_from_user(&vw, arg, sizeof(vw))) + return -EFAULT; + + /* stop capture */ + if (vw.flags || vw.width < 16 || vw.height < 16) { + saawrite((SAA7146_MC1_TR_E_1 << 16), + SAA7146_MC1); + return -EINVAL; + } + /* 32-bit align start and adjust width */ + if (saa->win.bpp < 4) { + int i = vw.x; + vw.x = (vw.x + 3) & ~3; + i = vw.x - i; + vw.width -= i; + } + saa->win.x = vw.x; + saa->win.y = vw.y; + saa->win.width = vw.width; + if (saa->win.width > 768) + saa->win.width = 768; + saa->win.height = vw.height; + if (CurrentMode == VIDEO_MODE_NTSC) { + if (saa->win.height > 480) + saa->win.height = 480; + } else { + if (saa->win.height > 576) + saa->win.height = 576; + } + + /* stop capture */ + saawrite((SAA7146_MC1_TR_E_1 << 16), SAA7146_MC1); + saa7146_set_winsize(saa); + + /* + * Do any clips. + */ + if (vw.clipcount < 0) { + if (copy_from_user(saa->dmavid2, vw.clips, + VIDEO_CLIPMAP_SIZE)) + return -EFAULT; + } else if (vw.clipcount > 16384) { + return -EINVAL; + } else if (vw.clipcount > 0) { + vcp = vmalloc(sizeof(struct video_clip) * + vw.clipcount); + if (vcp == NULL) + return -ENOMEM; + if (copy_from_user(vcp, vw.clips, + sizeof(struct video_clip) * + vw.clipcount)) { + vfree(vcp); + return -EFAULT; + } + } else /* nothing clipped */ + memset(saa->dmavid2, 0, VIDEO_CLIPMAP_SIZE); + + make_clip_tab(saa, vcp, vw.clipcount); + if (vw.clipcount > 0) + vfree(vcp); + + /* start capture & clip dma if we have an address */ + if ((saa->cap & 3) && saa->win.vidadr != 0) + saawrite(((SAA7146_MC1_TR_E_1 | + SAA7146_MC1_TR_E_2) << 16) | 0xffff, + SAA7146_MC1); + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + vw.x = saa->win.x; + vw.y = saa->win.y; + vw.width = saa->win.width; + vw.height = saa->win.height; + vw.chromakey = 0; + vw.flags = 0; + if (copy_to_user(arg, &vw, sizeof(vw))) + return -EFAULT; + return 0; + } + case VIDIOCCAPTURE: + { + int v; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if (v == 0) { + saa->cap &= ~1; + saawrite((SAA7146_MC1_TR_E_1 << 16), + SAA7146_MC1); + } else { + if (saa->win.vidadr == 0 || saa->win.width == 0 + || saa->win.height == 0) + return -EINVAL; + saa->cap |= 1; + saawrite((SAA7146_MC1_TR_E_1 << 16) | 0xffff, + SAA7146_MC1); + } + return 0; + } + case VIDIOCGFBUF: + { + struct video_buffer v; + v.base = (void *)saa->win.vidadr; + v.height = saa->win.sheight; + v.width = saa->win.swidth; + v.depth = saa->win.depth; + v.bytesperline = saa->win.bpl; + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + + } + case VIDIOCSFBUF: + { + struct video_buffer v; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if (v.depth != 8 && v.depth != 15 && v.depth != 16 && + v.depth != 24 && v.depth != 32 && v.width > 16 && + v.height > 16 && v.bytesperline > 16) + return -EINVAL; + if (v.base) + saa->win.vidadr = (unsigned long)v.base; + saa->win.sheight = v.height; + saa->win.swidth = v.width; + saa->win.bpp = ((v.depth + 7) & 0x38) / 8; + saa->win.depth = v.depth; + saa->win.bpl = v.bytesperline; + + DEBUG(printk("Display at %p is %d by %d, bytedepth %d, " + "bpl %d\n", v.base, v.width, v.height, + saa->win.bpp, saa->win.bpl)); + saa7146_set_winsize(saa); + return 0; + } + case VIDIOCKEY: + { + /* Will be handled higher up .. */ + return 0; + } + + case VIDIOCGAUDIO: + { + struct video_audio v; + v = saa->audio_dev; + v.flags &= ~(VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE); + v.flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME; + strcpy(v.name, "MPEG"); + v.mode = VIDEO_SOUND_STEREO; + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio v; + int i; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + i = (~(v.volume >> 8)) & 0xff; + if (!HaveCS4341) { + if (v.flags & VIDEO_AUDIO_MUTE) + debiwrite(saa, debNormal, + IBM_MP2_FRNT_ATTEN, 0xffff, 2); + if (!(v.flags & VIDEO_AUDIO_MUTE)) + debiwrite(saa, debNormal, + IBM_MP2_FRNT_ATTEN, 0x0000, 2); + if (v.flags & VIDEO_AUDIO_VOLUME) + debiwrite(saa, debNormal, + IBM_MP2_FRNT_ATTEN, + (i << 8) | i, 2); + } else { + if (v.flags & VIDEO_AUDIO_MUTE) + cs4341_setlevel(saa, 0xff, 0xff); + if (!(v.flags & VIDEO_AUDIO_MUTE)) + cs4341_setlevel(saa, 0, 0); + if (v.flags & VIDEO_AUDIO_VOLUME) + cs4341_setlevel(saa, i, i); + } + saa->audio_dev = v; + return 0; + } + + case VIDIOCGUNIT: + { + struct video_unit vu; + vu.video = saa->video_dev.minor; + vu.vbi = VIDEO_NO_UNIT; + vu.radio = VIDEO_NO_UNIT; + vu.audio = VIDEO_NO_UNIT; + vu.teletext = VIDEO_NO_UNIT; + if (copy_to_user(arg, &vu, sizeof(vu))) + return -EFAULT; + return 0; + } + case VIDIOCSPLAYMODE: + { + struct video_play_mode pmode; + if (copy_from_user((void *)&pmode, arg, + sizeof(struct video_play_mode))) + return -EFAULT; + switch (pmode.mode) { + case VID_PLAY_VID_OUT_MODE: + if (pmode.p1 != VIDEO_MODE_NTSC && + pmode.p1 != VIDEO_MODE_PAL) + return -EINVAL; + set_out_format(saa, pmode.p1); + return 0; + case VID_PLAY_GENLOCK: + debiwrite(saa, debNormal, XILINX_CTL0, + pmode.p1 ? 0x8000 : 0x8080, 2); + if (NewCard) + set_genlock_offset(saa, pmode.p2); + return 0; + case VID_PLAY_NORMAL: + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, ChipControl, 2); + ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_PAUSE: + /* IBM removed the PAUSE command */ + /* they say use SINGLE_FRAME now */ + case VID_PLAY_SINGLE_FRAME: + ibm_send_command(saa, IBM_MP2_SINGLE_FRAME,0,0); + if (saa->playmode == pmode.mode) { + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, + ChipControl, 2); + } + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_FAST_FORWARD: + ibm_send_command(saa, IBM_MP2_FAST_FORWARD,0,0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_SLOW_MOTION: + ibm_send_command(saa, IBM_MP2_SLOW_MOTION, + pmode.p1, 0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_IMMEDIATE_NORMAL: + /* ensure transfers resume */ + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, ChipControl, 2); + ibm_send_command(saa, IBM_MP2_IMED_NORM_PLAY, + 0, 0); + saa->playmode = VID_PLAY_NORMAL; + return 0; + case VID_PLAY_SWITCH_CHANNELS: + saa->audhead = saa->audtail = 0; + saa->vidhead = saa->vidtail = 0; + ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,1); + ibm_send_command(saa, IBM_MP2_RESET_AUD_RATE, + 0, 1); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, + 0, 2); + ibm_send_command(saa, IBM_MP2_CHANNEL_SWITCH, + 0, 1); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, + ChipControl, 2); + ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); + saa->playmode = VID_PLAY_NORMAL; + return 0; + case VID_PLAY_FREEZE_FRAME: + ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_STILL_MODE: + ibm_send_command(saa, IBM_MP2_SET_STILL_MODE, + 0, 0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_MASTER_MODE: + if (pmode.p1 == VID_PLAY_MASTER_NONE) + saa->boardcfg[1] = 0x13; + else if (pmode.p1 == VID_PLAY_MASTER_VIDEO) + saa->boardcfg[1] = 0x23; + else if (pmode.p1 == VID_PLAY_MASTER_AUDIO) + saa->boardcfg[1] = 0x43; + else + return -EINVAL; + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, ChipControl, 2); + return 0; + case VID_PLAY_ACTIVE_SCANLINES: + if (CurrentMode == VIDEO_MODE_PAL) { + if (pmode.p1 < 1 || pmode.p2 > 625) + return -EINVAL; + saa->boardcfg[5] = pmode.p1; + saa->boardcfg[55] = (pmode.p1 + + (pmode.p2 / 2) - 1) & 0xff; + } else { + if (pmode.p1 < 4 || pmode.p2 > 525) + return -EINVAL; + saa->boardcfg[4] = pmode.p1; + saa->boardcfg[54] = (pmode.p1 + + (pmode.p2 / 2) - 4) & 0xff; + } + set_out_format(saa, CurrentMode); + case VID_PLAY_RESET: + return do_ibm_reset(saa); + case VID_PLAY_END_MARK: + if (saa->endmarktail < saa->endmarkhead) { + if (saa->endmarkhead - + saa->endmarktail < 2) + return -ENOSPC; + } else if (saa->endmarkhead <=saa->endmarktail){ + if (saa->endmarktail - saa->endmarkhead + > (MAX_MARKS - 2)) + return -ENOSPC; + } else + return -ENOSPC; + saa->endmark[saa->endmarktail] = saa->audtail; + saa->endmarktail++; + if (saa->endmarktail >= MAX_MARKS) + saa->endmarktail = 0; + } + return -EINVAL; + } + case VIDIOCSWRITEMODE: + { + int mode; + if (copy_from_user((void *)&mode, arg, sizeof(int))) + return -EFAULT; + if (mode == VID_WRITE_MPEG_AUD || + mode == VID_WRITE_MPEG_VID || + mode == VID_WRITE_CC || + mode == VID_WRITE_TTX || + mode == VID_WRITE_OSD) { + saa->writemode = mode; + return 0; + } + return -EINVAL; + } + case VIDIOCSMICROCODE: + { + struct video_code ucode; + __u8 *udata; + int i; + if (copy_from_user(&ucode, arg, sizeof(ucode))) + return -EFAULT; + if (ucode.datasize > 65536 || ucode.datasize < 1024 || + strncmp(ucode.loadwhat, "dec", 3)) + return -EINVAL; + if ((udata = vmalloc(ucode.datasize)) == NULL) + return -ENOMEM; + if (copy_from_user(udata, ucode.data, ucode.datasize)) { + vfree(udata); + return -EFAULT; + } + ucode.data = udata; + if (!strncmp(ucode.loadwhat, "decoder.aud", 11) || + !strncmp(ucode.loadwhat, "decoder.vid", 11)) + i = initialize_ibmmpeg2(&ucode); + else + i = initialize_fpga(&ucode); + vfree(udata); + if (i) + return -EINVAL; + return 0; + + } + case VIDIOCGCHAN: /* this makes xawtv happy */ + { + struct video_channel v; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + v.flags = VIDEO_VC_AUDIO; + v.tuners = 0; + v.type = VID_TYPE_MPEG_DECODER; + v.norm = CurrentMode; + strcpy(v.name, "MPEG2"); + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSCHAN: /* this makes xawtv happy */ + { + struct video_channel v; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + /* do nothing */ + return 0; + } + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int saa_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct saa7146 *saa = file->private_data; + printk(KERN_DEBUG "stradis%d: saa_mmap called\n", saa->nr); + return -EINVAL; +} + +static ssize_t saa_read(struct file *file, char __user * buf, + size_t count, loff_t * ppos) +{ + return -EINVAL; +} + +static ssize_t saa_write(struct file *file, const char __user * buf, + size_t count, loff_t * ppos) +{ + struct saa7146 *saa = file->private_data; + unsigned long todo = count; + int blocksize, split; + unsigned long flags; + + while (todo > 0) { + if (saa->writemode == VID_WRITE_MPEG_AUD) { + spin_lock_irqsave(&saa->lock, flags); + if (saa->audhead <= saa->audtail) + blocksize = 65536 - + (saa->audtail - saa->audhead); + else + blocksize = saa->audhead - saa->audtail; + spin_unlock_irqrestore(&saa->lock, flags); + if (blocksize < 16384) { + saawrite(SAA7146_PSR_DEBI_S | + SAA7146_PSR_PIN1, SAA7146_IER); + saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); + /* wait for buffer space to open */ + interruptible_sleep_on(&saa->audq); + } + spin_lock_irqsave(&saa->lock, flags); + if (saa->audhead <= saa->audtail) { + blocksize = 65536 - + (saa->audtail - saa->audhead); + split = 65536 - saa->audtail; + } else { + blocksize = saa->audhead - saa->audtail; + split = 65536; + } + spin_unlock_irqrestore(&saa->lock, flags); + blocksize--; + if (blocksize > todo) + blocksize = todo; + /* double check that we really have space */ + if (!blocksize) + return -ENOSPC; + if (split < blocksize) { + if (copy_from_user(saa->audbuf + + saa->audtail, buf, split)) + return -EFAULT; + buf += split; + todo -= split; + blocksize -= split; + saa->audtail = 0; + } + if (copy_from_user(saa->audbuf + saa->audtail, buf, + blocksize)) + return -EFAULT; + saa->audtail += blocksize; + todo -= blocksize; + buf += blocksize; + saa->audtail &= 0xffff; + } else if (saa->writemode == VID_WRITE_MPEG_VID) { + spin_lock_irqsave(&saa->lock, flags); + if (saa->vidhead <= saa->vidtail) + blocksize = 524288 - + (saa->vidtail - saa->vidhead); + else + blocksize = saa->vidhead - saa->vidtail; + spin_unlock_irqrestore(&saa->lock, flags); + if (blocksize < 65536) { + saawrite(SAA7146_PSR_DEBI_S | + SAA7146_PSR_PIN1, SAA7146_IER); + saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); + /* wait for buffer space to open */ + interruptible_sleep_on(&saa->vidq); + } + spin_lock_irqsave(&saa->lock, flags); + if (saa->vidhead <= saa->vidtail) { + blocksize = 524288 - + (saa->vidtail - saa->vidhead); + split = 524288 - saa->vidtail; + } else { + blocksize = saa->vidhead - saa->vidtail; + split = 524288; + } + spin_unlock_irqrestore(&saa->lock, flags); + blocksize--; + if (blocksize > todo) + blocksize = todo; + /* double check that we really have space */ + if (!blocksize) + return -ENOSPC; + if (split < blocksize) { + if (copy_from_user(saa->vidbuf + + saa->vidtail, buf, split)) + return -EFAULT; + buf += split; + todo -= split; + blocksize -= split; + saa->vidtail = 0; + } + if (copy_from_user(saa->vidbuf + saa->vidtail, buf, + blocksize)) + return -EFAULT; + saa->vidtail += blocksize; + todo -= blocksize; + buf += blocksize; + saa->vidtail &= 0x7ffff; + } else if (saa->writemode == VID_WRITE_OSD) { + if (count > 131072) + return -ENOSPC; + if (copy_from_user(saa->osdbuf, buf, count)) + return -EFAULT; + buf += count; + saa->osdhead = 0; + saa->osdtail = count; + debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_OSD_LINK_ADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00d, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, + debiread(saa, debNormal, + IBM_MP2_DISP_MODE, 2) | 1, 2); + /* trigger osd data transfer */ + saawrite(SAA7146_PSR_DEBI_S | + SAA7146_PSR_PIN1, SAA7146_IER); + saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); + } + } + return count; +} + +static int saa_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146 *saa = container_of(vdev, struct saa7146, video_dev); + + lock_kernel(); + file->private_data = saa; + + saa->user++; + if (saa->user > 1) { + unlock_kernel(); + return 0; /* device open already, don't reset */ + } + saa->writemode = VID_WRITE_MPEG_VID; /* default to video */ + unlock_kernel(); + return 0; +} + +static int saa_release(struct file *file) +{ + struct saa7146 *saa = file->private_data; + saa->user--; + + if (saa->user > 0) /* still someone using device */ + return 0; + saawrite(0x007f0000, SAA7146_MC1); /* stop all overlay dma */ + return 0; +} + +static const struct v4l2_file_operations saa_fops = { + .owner = THIS_MODULE, + .open = saa_open, + .release = saa_release, + .ioctl = saa_ioctl, + .read = saa_read, + .write = saa_write, + .mmap = saa_mmap, +}; + +/* template for video_device-structure */ +static struct video_device saa_template = { + .name = "SAA7146A", + .fops = &saa_fops, + .release = video_device_release_empty, +}; + +static int __devinit configure_saa7146(struct pci_dev *pdev, int num) +{ + int retval; + struct saa7146 *saa = pci_get_drvdata(pdev); + + saa->endmarkhead = saa->endmarktail = 0; + saa->win.x = saa->win.y = 0; + saa->win.width = saa->win.cropwidth = 720; + saa->win.height = saa->win.cropheight = 480; + saa->win.cropx = saa->win.cropy = 0; + saa->win.bpp = 2; + saa->win.depth = 16; + saa->win.color_fmt = palette2fmt[VIDEO_PALETTE_RGB565]; + saa->win.bpl = 1024 * saa->win.bpp; + saa->win.swidth = 1024; + saa->win.sheight = 768; + saa->picture.brightness = 32768; + saa->picture.contrast = 38768; + saa->picture.colour = 32768; + saa->cap = 0; + saa->nr = num; + saa->playmode = VID_PLAY_NORMAL; + memset(saa->boardcfg, 0, 64); /* clear board config area */ + saa->saa7146_mem = NULL; + saa->dmavid1 = saa->dmavid2 = saa->dmavid3 = saa->dmaa1in = + saa->dmaa1out = saa->dmaa2in = saa->dmaa2out = + saa->pagevid1 = saa->pagevid2 = saa->pagevid3 = saa->pagea1in = + saa->pagea1out = saa->pagea2in = saa->pagea2out = + saa->pagedebi = saa->dmaRPS1 = saa->dmaRPS2 = saa->pageRPS1 = + saa->pageRPS2 = NULL; + saa->audbuf = saa->vidbuf = saa->osdbuf = saa->dmadebi = NULL; + saa->audhead = saa->vidtail = 0; + + init_waitqueue_head(&saa->i2cq); + init_waitqueue_head(&saa->audq); + init_waitqueue_head(&saa->debiq); + init_waitqueue_head(&saa->vidq); + spin_lock_init(&saa->lock); + + retval = pci_enable_device(pdev); + if (retval) { + dev_err(&pdev->dev, "%d: pci_enable_device failed!\n", num); + goto err; + } + + saa->id = pdev->device; + saa->irq = pdev->irq; + saa->saa7146_adr = pci_resource_start(pdev, 0); + pci_read_config_byte(pdev, PCI_CLASS_REVISION, &saa->revision); + + saa->saa7146_mem = ioremap(saa->saa7146_adr, 0x200); + if (saa->saa7146_mem == NULL) { + dev_err(&pdev->dev, "%d: ioremap failed!\n", num); + retval = -EIO; + goto err; + } + + memcpy(&saa->video_dev, &saa_template, sizeof(saa_template)); + saawrite(0, SAA7146_IER); /* turn off all interrupts */ + + retval = request_irq(saa->irq, saa7146_irq, IRQF_SHARED | IRQF_DISABLED, + "stradis", saa); + if (retval == -EINVAL) + dev_err(&pdev->dev, "%d: Bad irq number or handler\n", num); + else if (retval == -EBUSY) + dev_err(&pdev->dev, "%d: IRQ %ld busy, change your PnP config " + "in BIOS\n", num, saa->irq); + if (retval < 0) + goto errio; + + pci_set_master(pdev); + retval = video_register_device(&saa->video_dev, VFL_TYPE_GRABBER, + video_nr); + if (retval < 0) { + dev_err(&pdev->dev, "%d: error in registering video device!\n", + num); + goto errio; + } + + return 0; +errio: + iounmap(saa->saa7146_mem); +err: + return retval; +} + +static int __devinit init_saa7146(struct pci_dev *pdev) +{ + struct saa7146 *saa = pci_get_drvdata(pdev); + + saa->user = 0; + /* reset the saa7146 */ + saawrite(0xffff0000, SAA7146_MC1); + mdelay(5); + /* enable debi and i2c transfers and pins */ + saawrite(((SAA7146_MC1_EDP | SAA7146_MC1_EI2C | + SAA7146_MC1_TR_E_DEBI) << 16) | 0xffff, SAA7146_MC1); + /* ensure proper state of chip */ + saawrite(0x00000000, SAA7146_PAGE1); + saawrite(0x00f302c0, SAA7146_NUM_LINE_BYTE1); + saawrite(0x00000000, SAA7146_PAGE2); + saawrite(0x01400080, SAA7146_NUM_LINE_BYTE2); + saawrite(0x00000000, SAA7146_DD1_INIT); + saawrite(0x00000000, SAA7146_DD1_STREAM_B); + saawrite(0x00000000, SAA7146_DD1_STREAM_A); + saawrite(0x00000000, SAA7146_BRS_CTRL); + saawrite(0x80400040, SAA7146_BCS_CTRL); + saawrite(0x0000e000 /*| (1<<29) */ , SAA7146_HPS_CTRL); + saawrite(0x00000060, SAA7146_CLIP_FORMAT_CTRL); + saawrite(0x00000000, SAA7146_ACON1); + saawrite(0x00000000, SAA7146_ACON2); + saawrite(0x00000600, SAA7146_I2C_STATUS); + saawrite(((SAA7146_MC2_UPLD_D1_B | SAA7146_MC2_UPLD_D1_A | + SAA7146_MC2_UPLD_BRS | SAA7146_MC2_UPLD_HPS_H | + SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_DMA2 | + SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_I2C) << 16) | 0xffff, + SAA7146_MC2); + /* setup arbitration control registers */ + saawrite(0x1412121a, SAA7146_PCI_BT_V1); + + /* allocate 32k dma buffer + 4k for page table */ + if ((saa->dmadebi = kmalloc(32768 + 4096, GFP_KERNEL)) == NULL) { + dev_err(&pdev->dev, "%d: debi kmalloc failed\n", saa->nr); + goto err; + } +#if 0 + saa->pagedebi = saa->dmadebi + 32768; /* top 4k is for mmu */ + saawrite(virt_to_bus(saa->pagedebi) /*|0x800 */ , SAA7146_DEBI_PAGE); + for (i = 0; i < 12; i++) /* setup mmu page table */ + saa->pagedebi[i] = virt_to_bus((saa->dmadebi + i * 4096)); +#endif + saa->audhead = saa->vidhead = saa->osdhead = 0; + saa->audtail = saa->vidtail = saa->osdtail = 0; + if (saa->vidbuf == NULL && (saa->vidbuf = vmalloc(524288)) == NULL) { + dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); + goto err; + } + if (saa->audbuf == NULL && (saa->audbuf = vmalloc(65536)) == NULL) { + dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); + goto errfree; + } + if (saa->osdbuf == NULL && (saa->osdbuf = vmalloc(131072)) == NULL) { + dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); + goto errfree; + } + /* allocate 81920 byte buffer for clipping */ + if ((saa->dmavid2 = kzalloc(VIDEO_CLIPMAP_SIZE, GFP_KERNEL)) == NULL) { + dev_err(&pdev->dev, "%d: clip kmalloc failed\n", saa->nr); + goto errfree; + } + /* setup clipping registers */ + saawrite(virt_to_bus(saa->dmavid2), SAA7146_BASE_EVEN2); + saawrite(virt_to_bus(saa->dmavid2) + 128, SAA7146_BASE_ODD2); + saawrite(virt_to_bus(saa->dmavid2) + VIDEO_CLIPMAP_SIZE, + SAA7146_PROT_ADDR2); + saawrite(256, SAA7146_PITCH2); + saawrite(4, SAA7146_PAGE2); /* dma direction: read, no byteswap */ + saawrite(((SAA7146_MC2_UPLD_DMA2) << 16) | SAA7146_MC2_UPLD_DMA2, + SAA7146_MC2); + I2CBusScan(saa); + + return 0; +errfree: + vfree(saa->osdbuf); + vfree(saa->audbuf); + vfree(saa->vidbuf); + saa->audbuf = saa->osdbuf = saa->vidbuf = NULL; +err: + return -ENOMEM; +} + +static void stradis_release_saa(struct pci_dev *pdev) +{ + u8 command; + struct saa7146 *saa = pci_get_drvdata(pdev); + + /* turn off all capturing, DMA and IRQs */ + saawrite(0xffff0000, SAA7146_MC1); /* reset chip */ + saawrite(0, SAA7146_MC2); + saawrite(0, SAA7146_IER); + saawrite(0xffffffffUL, SAA7146_ISR); + + /* disable PCI bus-mastering */ + pci_read_config_byte(pdev, PCI_COMMAND, &command); + command &= ~PCI_COMMAND_MASTER; + pci_write_config_byte(pdev, PCI_COMMAND, command); + + /* unmap and free memory */ + saa->audhead = saa->audtail = saa->osdhead = 0; + saa->vidhead = saa->vidtail = saa->osdtail = 0; + vfree(saa->vidbuf); + vfree(saa->audbuf); + vfree(saa->osdbuf); + kfree(saa->dmavid2); + saa->audbuf = saa->vidbuf = saa->osdbuf = NULL; + saa->dmavid2 = NULL; + kfree(saa->dmadebi); + kfree(saa->dmavid1); + kfree(saa->dmavid3); + kfree(saa->dmaa1in); + kfree(saa->dmaa1out); + kfree(saa->dmaa2in); + kfree(saa->dmaa2out); + kfree(saa->dmaRPS1); + kfree(saa->dmaRPS2); + free_irq(saa->irq, saa); + if (saa->saa7146_mem) + iounmap(saa->saa7146_mem); + if (video_is_registered(&saa->video_dev)) + video_unregister_device(&saa->video_dev); +} + +static int __devinit stradis_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int retval = -EINVAL; + + if (saa_num >= SAA7146_MAX) + goto err; + + if (!pdev->subsystem_vendor) + dev_info(&pdev->dev, "%d: rev1 decoder\n", saa_num); + else + dev_info(&pdev->dev, "%d: SDM2xx found\n", saa_num); + + pci_set_drvdata(pdev, &saa7146s[saa_num]); + + retval = configure_saa7146(pdev, saa_num); + if (retval) { + dev_err(&pdev->dev, "%d: error in configuring\n", saa_num); + goto err; + } + + if (init_saa7146(pdev) < 0) { + dev_err(&pdev->dev, "%d: error in initialization\n", saa_num); + retval = -EIO; + goto errrel; + } + + saa_num++; + + return 0; +errrel: + stradis_release_saa(pdev); +err: + return retval; +} + +static void __devexit stradis_remove(struct pci_dev *pdev) +{ + stradis_release_saa(pdev); +} + +static struct pci_device_id stradis_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146) }, + { 0 } +}; + + +static struct pci_driver stradis_driver = { + .name = "stradis", + .id_table = stradis_pci_tbl, + .probe = stradis_probe, + .remove = __devexit_p(stradis_remove) +}; + +static int __init stradis_init(void) +{ + int retval; + + saa_num = 0; + + retval = pci_register_driver(&stradis_driver); + if (retval) + printk(KERN_ERR "stradis: Unable to register pci driver.\n"); + + return retval; +} + +static void __exit stradis_exit(void) +{ + pci_unregister_driver(&stradis_driver); + printk(KERN_INFO "stradis: module cleanup complete\n"); +} + +module_init(stradis_init); +module_exit(stradis_exit); -- cgit v1.2.3-70-g09d2 From d69f27186c16008540166c8017e9d4db2b477588 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 26 Sep 2010 08:16:56 -0300 Subject: V4L/DVB: v4l2-dev: after a disconnect any ioctl call will be blocked Until now all fops except release and (unlocked_)ioctl returned an error after the device node was unregistered. Extend this as well to the ioctl fops. There is nothing useful that an application can do here and it complicates the driver code unnecessarily. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 5 ++--- drivers/media/video/v4l2-dev.c | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 8fb9de4f4875..9b1d81c26b7d 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -551,9 +551,8 @@ from /dev). After video_unregister_device() returns no new opens can be done. However, in the case of USB devices some application might still have one of these -device nodes open. So after the unregister all file operations will return -an error as well, except for the ioctl and unlocked_ioctl file operations: -those will still be passed on since some buffer ioctls may still be needed. +device nodes open. So after the unregister all file operations (except +release, of course) will return an error as well. When the last user of the video device node exits, then the vdev->release() callback is called and you can do the final cleanup there. diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index d4a353260c1e..f069c61cdf68 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -221,8 +221,8 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct video_device *vdev = video_devdata(filp); int ret; - /* Allow ioctl to continue even if the device was unregistered. - Things like dequeueing buffers might still be useful. */ + if (!vdev->fops->ioctl) + return -ENOTTY; if (vdev->fops->unlocked_ioctl) { ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); } else if (vdev->fops->ioctl) { -- cgit v1.2.3-70-g09d2 From ee6869afc922a9849979e49bb3bbcad794872fcb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 26 Sep 2010 08:47:38 -0300 Subject: V4L/DVB: v4l2: add core serialization lock Drivers can optionally set a pointer to a mutex in struct video_device. The core will use that to lock before calling open, read, write, unlocked_ioctl, poll, mmap or release. Updated the documentation as well and ensure that v4l2-event knows about the lock: it will unlock it before doing a blocking wait on an event and relock it afterwards. Ensure that the 'video_is_registered' check is done when the lock is held: a typical disconnect will take the lock as well before unregistering the device nodes, so to prevent race conditions the video_is_registered check should also be done with the lock held. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 20 ++++++++ drivers/media/video/v4l2-dev.c | 76 +++++++++++++++++++++------- drivers/media/video/v4l2-event.c | 9 +++- include/media/v4l2-dev.h | 3 ++ 4 files changed, 89 insertions(+), 19 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 9b1d81c26b7d..a128e012a45c 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -453,6 +453,10 @@ You should also set these fields: - ioctl_ops: if you use the v4l2_ioctl_ops to simplify ioctl maintenance (highly recommended to use this and it might become compulsory in the future!), then set this to your v4l2_ioctl_ops struct. +- lock: leave to NULL if you want to do all the locking in the driver. + Otherwise you give it a pointer to a struct mutex_lock and before any + of the v4l2_file_operations is called this lock will be taken by the + core and released afterwards. - parent: you only set this if v4l2_device was registered with NULL as the parent device struct. This only happens in cases where one hardware device has multiple PCI devices that all share the same v4l2_device core. @@ -469,6 +473,22 @@ If you use v4l2_ioctl_ops, then you should set either .unlocked_ioctl or The v4l2_file_operations struct is a subset of file_operations. The main difference is that the inode argument is omitted since it is never used. +v4l2_file_operations and locking +-------------------------------- + +You can set a pointer to a mutex_lock in struct video_device. Usually this +will be either a top-level mutex or a mutex per device node. If you want +finer-grained locking then you have to set it to NULL and do you own locking. + +If a lock is specified then all file operations will be serialized on that +lock. If you use videobuf then you must pass the same lock to the videobuf +queue initialize function: if videobuf has to wait for a frame to arrive, then +it will temporarily unlock the lock and relock it afterwards. If your driver +also waits in the code, then you should do the same to allow other processes +to access the device node while the first process is waiting for something. + +The implementation of a hotplug disconnect should also take the lock before +calling v4l2_device_disconnect and video_unregister_device. video_device registration ------------------------- diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 5a54eabd4c42..a7702e3d149c 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -187,33 +187,50 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf, size_t sz, loff_t *off) { struct video_device *vdev = video_devdata(filp); + int ret = -EIO; if (!vdev->fops->read) return -EINVAL; - if (!video_is_registered(vdev)) - return -EIO; - return vdev->fops->read(filp, buf, sz, off); + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->read(filp, buf, sz, off); + if (vdev->lock) + mutex_unlock(vdev->lock); + return ret; } static ssize_t v4l2_write(struct file *filp, const char __user *buf, size_t sz, loff_t *off) { struct video_device *vdev = video_devdata(filp); + int ret = -EIO; if (!vdev->fops->write) return -EINVAL; - if (!video_is_registered(vdev)) - return -EIO; - return vdev->fops->write(filp, buf, sz, off); + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->write(filp, buf, sz, off); + if (vdev->lock) + mutex_unlock(vdev->lock); + return ret; } static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) { struct video_device *vdev = video_devdata(filp); + int ret = DEFAULT_POLLMASK; - if (!vdev->fops->poll || !video_is_registered(vdev)) - return DEFAULT_POLLMASK; - return vdev->fops->poll(filp, poll); + if (!vdev->fops->poll) + return ret; + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->poll(filp, poll); + if (vdev->lock) + mutex_unlock(vdev->lock); + return ret; } static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) @@ -224,7 +241,11 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (!vdev->fops->ioctl) return -ENOTTY; if (vdev->fops->unlocked_ioctl) { + if (vdev->lock) + mutex_lock(vdev->lock); ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); + if (vdev->lock) + mutex_unlock(vdev->lock); } else if (vdev->fops->ioctl) { /* TODO: convert all drivers to unlocked_ioctl */ lock_kernel(); @@ -239,10 +260,17 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) { struct video_device *vdev = video_devdata(filp); + int ret = -ENODEV; - if (!vdev->fops->mmap || !video_is_registered(vdev)) - return -ENODEV; - return vdev->fops->mmap(filp, vm); + if (!vdev->fops->mmap) + return ret; + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->mmap(filp, vm); + if (vdev->lock) + mutex_unlock(vdev->lock); + return ret; } /* Override for the open function */ @@ -254,17 +282,24 @@ static int v4l2_open(struct inode *inode, struct file *filp) /* Check if the video device is available */ mutex_lock(&videodev_lock); vdev = video_devdata(filp); - /* return ENODEV if the video device has been removed - already or if it is not registered anymore. */ - if (vdev == NULL || !video_is_registered(vdev)) { + /* return ENODEV if the video device has already been removed. */ + if (vdev == NULL) { mutex_unlock(&videodev_lock); return -ENODEV; } /* and increase the device refcount */ video_get(vdev); mutex_unlock(&videodev_lock); - if (vdev->fops->open) - ret = vdev->fops->open(filp); + if (vdev->fops->open) { + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->open(filp); + else + ret = -ENODEV; + if (vdev->lock) + mutex_unlock(vdev->lock); + } /* decrease the refcount in case of an error */ if (ret) @@ -278,8 +313,13 @@ static int v4l2_release(struct inode *inode, struct file *filp) struct video_device *vdev = video_devdata(filp); int ret = 0; - if (vdev->fops->release) + if (vdev->fops->release) { + if (vdev->lock) + mutex_lock(vdev->lock); vdev->fops->release(filp); + if (vdev->lock) + mutex_unlock(vdev->lock); + } /* decrease the refcount unconditionally since the release() return value is ignored. */ diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c index de74ce07b5e2..69fd343d4774 100644 --- a/drivers/media/video/v4l2-event.c +++ b/drivers/media/video/v4l2-event.c @@ -134,15 +134,22 @@ int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event, if (nonblocking) return __v4l2_event_dequeue(fh, event); + /* Release the vdev lock while waiting */ + if (fh->vdev->lock) + mutex_unlock(fh->vdev->lock); + do { ret = wait_event_interruptible(events->wait, events->navailable != 0); if (ret < 0) - return ret; + break; ret = __v4l2_event_dequeue(fh, event); } while (ret == -ENOENT); + if (fh->vdev->lock) + mutex_lock(fh->vdev->lock); + return ret; } EXPORT_SYMBOL_GPL(v4l2_event_dequeue); diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index ba236ff35c8a..15802a067a12 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -94,6 +94,9 @@ struct video_device /* ioctl callbacks */ const struct v4l2_ioctl_ops *ioctl_ops; + + /* serialization lock */ + struct mutex *lock; }; /* dev to video-device */ -- cgit v1.2.3-70-g09d2 From 9c84d89bf13b702ca0e0db5eb3880300c4bd58f4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 11 Oct 2010 12:36:37 -0300 Subject: [media] radio-mr800: fix locking order Don't hold the lock before unregistering the device, since when the device is unregistered the datastruct containing the lock may be freed (if the refcount went to 0). Also fixed the framework documentation that erroneously suggested the wrong locking order as well. Reported-by: David Ellingsworth Signed-off-by: Hans Verkuil Acked-by: David Ellingsworth Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 2 +- drivers/media/radio/radio-mr800.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index a128e012a45c..f22f35c271f3 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -488,7 +488,7 @@ also waits in the code, then you should do the same to allow other processes to access the device node while the first process is waiting for something. The implementation of a hotplug disconnect should also take the lock before -calling v4l2_device_disconnect and video_unregister_device. +calling v4l2_device_disconnect. video_device registration ------------------------- diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index 97967ad3cd12..2f56b26cece9 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -285,8 +285,8 @@ static void usb_amradio_disconnect(struct usb_interface *intf) mutex_lock(&radio->lock); v4l2_device_disconnect(&radio->v4l2_dev); - video_unregister_device(&radio->videodev); mutex_unlock(&radio->lock); + video_unregister_device(&radio->videodev); } /* vidioc_querycap - query device capabilities */ -- cgit v1.2.3-70-g09d2 From 1e55659ce6ddb5247cee0b1f720d77a799902b85 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 19 Oct 2010 09:32:04 +0000 Subject: can-raw: add msg_flags to distinguish local traffic CAN has no addressing scheme. It is currently impossible for userspace to tell is a received CAN frame comes from another process on the local host, or from a remote CAN device. This patch add support for userspace applications to distinguish between 'own', 'local' and 'remote' CAN traffic. The distinction is made by returning flags in msg->msg_flags in the call to recvmsg(). The added documentation explains the introduced flags. Signed-off-by: Kurt Van Dijck Signed-off-by: Oliver Hartkopp Signed-off-by: David S. Miller --- Documentation/networking/can.txt | 12 ++++++++++++ net/can/raw.c | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/can.txt b/Documentation/networking/can.txt index cd79735013f9..5b04b67ddca2 100644 --- a/Documentation/networking/can.txt +++ b/Documentation/networking/can.txt @@ -22,6 +22,7 @@ This file contains 4.1.2 RAW socket option CAN_RAW_ERR_FILTER 4.1.3 RAW socket option CAN_RAW_LOOPBACK 4.1.4 RAW socket option CAN_RAW_RECV_OWN_MSGS + 4.1.5 RAW socket returned message flags 4.2 Broadcast Manager protocol sockets (SOCK_DGRAM) 4.3 connected transport protocols (SOCK_SEQPACKET) 4.4 unconnected transport protocols (SOCK_DGRAM) @@ -471,6 +472,17 @@ solution for a couple of reasons: setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &recv_own_msgs, sizeof(recv_own_msgs)); + 4.1.5 RAW socket returned message flags + + When using recvmsg() call, the msg->msg_flags may contain following flags: + + MSG_DONTROUTE: set when the received frame was created on the local host. + + MSG_CONFIRM: set when the frame was sent via the socket it is received on. + This flag can be interpreted as a 'transmission confirmation' when the + CAN driver supports the echo of frames on driver level, see 3.2 and 6.2. + In order to receive such messages, CAN_RAW_RECV_OWN_MSGS must be set. + 4.2 Broadcast Manager protocol sockets (SOCK_DGRAM) 4.3 connected transport protocols (SOCK_SEQPACKET) 4.4 unconnected transport protocols (SOCK_DGRAM) diff --git a/net/can/raw.c b/net/can/raw.c index 7d77e67e57af..e88f610fdb7b 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -90,23 +90,39 @@ struct raw_sock { can_err_mask_t err_mask; }; +/* + * Return pointer to store the extra msg flags for raw_recvmsg(). + * We use the space of one unsigned int beyond the 'struct sockaddr_can' + * in skb->cb. + */ +static inline unsigned int *raw_flags(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(skb->cb) <= (sizeof(struct sockaddr_can) + + sizeof(unsigned int))); + + /* return pointer after struct sockaddr_can */ + return (unsigned int *)(&((struct sockaddr_can *)skb->cb)[1]); +} + static inline struct raw_sock *raw_sk(const struct sock *sk) { return (struct raw_sock *)sk; } -static void raw_rcv(struct sk_buff *skb, void *data) +static void raw_rcv(struct sk_buff *oskb, void *data) { struct sock *sk = (struct sock *)data; struct raw_sock *ro = raw_sk(sk); struct sockaddr_can *addr; + struct sk_buff *skb; + unsigned int *pflags; /* check the received tx sock reference */ - if (!ro->recv_own_msgs && skb->sk == sk) + if (!ro->recv_own_msgs && oskb->sk == sk) return; /* clone the given skb to be able to enqueue it into the rcv queue */ - skb = skb_clone(skb, GFP_ATOMIC); + skb = skb_clone(oskb, GFP_ATOMIC); if (!skb) return; @@ -123,6 +139,14 @@ static void raw_rcv(struct sk_buff *skb, void *data) addr->can_family = AF_CAN; addr->can_ifindex = skb->dev->ifindex; + /* add CAN specific message flags for raw_recvmsg() */ + pflags = raw_flags(skb); + *pflags = 0; + if (oskb->sk) + *pflags |= MSG_DONTROUTE; + if (oskb->sk == sk) + *pflags |= MSG_CONFIRM; + if (sock_queue_rcv_skb(sk, skb) < 0) kfree_skb(skb); } @@ -707,6 +731,9 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock, memcpy(msg->msg_name, skb->cb, msg->msg_namelen); } + /* assign the flags that have been recorded in raw_rcv() */ + msg->msg_flags |= *(raw_flags(skb)); + skb_free_datagram(sk, skb); return size; -- cgit v1.2.3-70-g09d2 From 4d1529043705737ba442f5a0048043718e601ce4 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Thu, 21 Oct 2010 14:54:19 +0200 Subject: Added blk-lib.c and blk-barrier.c was renamed to blk-flush.c Signed-off-by: Mike Snitzer Signed-off-by: Jens Axboe --- Documentation/DocBook/kernel-api.tmpl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index 6899f471fb15..6b4e07f28b69 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -257,7 +257,8 @@ X!Earch/x86/kernel/mca_32.c !Iblock/blk-sysfs.c !Eblock/blk-settings.c !Eblock/blk-exec.c -!Eblock/blk-barrier.c +!Eblock/blk-flush.c +!Eblock/blk-lib.c !Eblock/blk-tag.c !Iblock/blk-tag.c !Eblock/blk-integrity.c -- cgit v1.2.3-70-g09d2 From 35f0ce032b0f2d6974da516b5a113f49b7b70b09 Mon Sep 17 00:00:00 2001 From: Vernon Mauery Date: Tue, 5 Oct 2010 15:47:18 -0700 Subject: IBM Real-Time "SMI Free" mode driver -v7 After a period of RFC for this driver, I think it is ready for inclusion in the platform-driver-x86 tree, hopefully to be staged in the next merge window into Linus's tree. --Vernon ------------------------------------------------------------ IBM Real-Time "SMI Free" mode driver This driver supports the Real-Time Linux (RTL) BIOS feature. The RTL feature allows non-fatal System Management Interrupts (SMIs) to be disabled on supported IBM platforms and is intended to be coupled with a user-space daemon to monitor the hardware in a way that can be prioritized and scheduled to better suit the requirements for the system. The Device is presented as a special "_RTL_" table to the OS in the Extended BIOS Data Area. There is a simple protocol for entering and exiting the mode at runtime. This driver creates a simple sysfs interface to allow a simple entry and exit from RTL mode in the UFI/BIOS. Since the driver is specific to IBM SystemX hardware (x86- based servers) it only builds on x86 builds. To reduce the risk of loading on the wrong hardware, the module uses DMI information and checks a list of servers that are known to work. Signed-off-by: Vernon Mauery Signed-off-by: Matthew Garrett --- .../ABI/testing/sysfs-devices-system-ibm-rtl | 22 ++ drivers/platform/x86/Kconfig | 16 + drivers/platform/x86/Makefile | 2 +- drivers/platform/x86/ibm_rtl.c | 341 +++++++++++++++++++++ 4 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-devices-system-ibm-rtl create mode 100644 drivers/platform/x86/ibm_rtl.c (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl b/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl new file mode 100644 index 000000000000..b82deeaec314 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl @@ -0,0 +1,22 @@ +What: state +Date: Sep 2010 +KernelVersion: 2.6.37 +Contact: Vernon Mauery +Description: The state file allows a means by which to change in and + out of Premium Real-Time Mode (PRTM), as well as the + ability to query the current state. + 0 => PRTM off + 1 => PRTM enabled +Users: The ibm-prtm userspace daemon uses this interface. + + +What: version +Date: Sep 2010 +KernelVersion: 2.6.37 +Contact: Vernon Mauery +Description: The version file provides a means by which to query + the RTL table version that lives in the Extended + BIOS Data Area (EBDA). +Users: The ibm-prtm userspace daemon uses this interface. + + diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 91e431b6f20f..faec777b1ed4 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -615,6 +615,22 @@ config INTEL_IPS functionality. If in doubt, say Y here; it will only load on supported platforms. +config IBM_RTL + tristate "Device driver to enable PRTL support" + depends on X86 && PCI + ---help--- + Enable support for IBM Premium Real Time Mode (PRTM). + This module will allow you the enter and exit PRTM in the BIOS via + sysfs on platforms that support this feature. System in PRTM will + not receive CPU-generated SMIs for recoverable errors. Use of this + feature without proper support may void your hardware warranty. + + If the proper BIOS support is found the driver will load and create + /sys/devices/system/ibm_rtl/. The "state" variable will indicate + whether or not the BIOS is in PRTM. + state = 0 (BIOS SMIs on) + state = 1 (BIOS SMIs off) + config XO1_RFKILL tristate "OLPC XO-1 software RF kill switch" depends on OLPC diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index c15e96336e65..9950ccc940b5 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -32,4 +32,4 @@ obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o obj-$(CONFIG_INTEL_IPS) += intel_ips.o obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o - +obj-$(CONFIG_IBM_RTL) += ibm_rtl.o diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c new file mode 100644 index 000000000000..3c2c6b91ecb3 --- /dev/null +++ b/drivers/platform/x86/ibm_rtl.c @@ -0,0 +1,341 @@ +/* + * IBM Real-Time Linux driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2010 + * + * Author: Keith Mannthey + * Vernon Mauery + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static bool force; +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); + +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Show debug output"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Keith Mannthey "); +MODULE_AUTHOR("Vernon Mauery "); + +#define RTL_ADDR_TYPE_IO 1 +#define RTL_ADDR_TYPE_MMIO 2 + +#define RTL_CMD_ENTER_PRTM 1 +#define RTL_CMD_EXIT_PRTM 2 + +/* The RTL table as presented by the EBDA: */ +struct ibm_rtl_table { + char signature[5]; /* signature should be "_RTL_" */ + u8 version; + u8 rt_status; + u8 command; + u8 command_status; + u8 cmd_address_type; + u8 cmd_granularity; + u8 cmd_offset; + u16 reserve1; + u32 cmd_port_address; /* platform dependent address */ + u32 cmd_port_value; /* platform dependent value */ +} __attribute__((packed)); + +/* to locate "_RTL_" signature do a masked 5-byte integer compare */ +#define RTL_SIGNATURE 0x0000005f4c54525fULL +#define RTL_MASK 0x000000ffffffffffULL + +#define RTL_DEBUG(A, ...) do { \ + if (debug) \ + pr_info("ibm-rtl: " A, ##__VA_ARGS__ ); \ +} while (0) + +static DEFINE_MUTEX(rtl_lock); +static struct ibm_rtl_table __iomem *rtl_table; +static void __iomem *ebda_map; +static void __iomem *rtl_cmd_addr; +static u8 rtl_cmd_type; +static u8 rtl_cmd_width; + +static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len) +{ + if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO) + return ioremap(addr, len); + return ioport_map(addr, len); +} + +static void rtl_port_unmap(void __iomem *addr) +{ + if (addr && rtl_cmd_type == RTL_ADDR_TYPE_MMIO) + iounmap(addr); + else + ioport_unmap(addr); +} + +static int ibm_rtl_write(u8 value) +{ + int ret = 0, count = 0; + static u32 cmd_port_val; + + RTL_DEBUG("%s(%d)\n", __FUNCTION__, value); + + value = value == 1 ? RTL_CMD_ENTER_PRTM : RTL_CMD_EXIT_PRTM; + + mutex_lock(&rtl_lock); + + if (ioread8(&rtl_table->rt_status) != value) { + iowrite8(value, &rtl_table->command); + + switch (rtl_cmd_width) { + case 8: + cmd_port_val = ioread8(&rtl_table->cmd_port_value); + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); + iowrite8((u8)cmd_port_val, rtl_cmd_addr); + break; + case 16: + cmd_port_val = ioread16(&rtl_table->cmd_port_value); + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); + iowrite16((u16)cmd_port_val, rtl_cmd_addr); + break; + case 32: + cmd_port_val = ioread32(&rtl_table->cmd_port_value); + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); + iowrite32(cmd_port_val, rtl_cmd_addr); + break; + } + + while (ioread8(&rtl_table->command)) { + msleep(10); + if (count++ > 500) { + pr_err("ibm-rtl: Hardware not responding to " + "mode switch request\n"); + ret = -EIO; + break; + } + + } + + if (ioread8(&rtl_table->command_status)) { + RTL_DEBUG("command_status reports failed command\n"); + ret = -EIO; + } + } + + mutex_unlock(&rtl_lock); + return ret; +} + +static ssize_t rtl_show_version(struct sysdev_class * dev, + struct sysdev_class_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", (int)ioread8(&rtl_table->version)); +} + +static ssize_t rtl_show_state(struct sysdev_class *dev, + struct sysdev_class_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", ioread8(&rtl_table->rt_status)); +} + +static ssize_t rtl_set_state(struct sysdev_class *dev, + struct sysdev_class_attribute *attr, + const char *buf, + size_t count) +{ + ssize_t ret; + + if (count < 1 || count > 2) + return -EINVAL; + + switch (buf[0]) { + case '0': + ret = ibm_rtl_write(0); + break; + case '1': + ret = ibm_rtl_write(1); + break; + default: + ret = -EINVAL; + } + if (ret >= 0) + ret = count; + + return ret; +} + +static struct sysdev_class class_rtl = { + .name = "ibm_rtl", +}; + +static SYSDEV_CLASS_ATTR(version, S_IRUGO, rtl_show_version, NULL); +static SYSDEV_CLASS_ATTR(state, 0600, rtl_show_state, rtl_set_state); + +static struct sysdev_class_attribute *rtl_attributes[] = { + &attr_version, + &attr_state, + NULL +}; + + +static int rtl_setup_sysfs(void) { + int ret, i; + ret = sysdev_class_register(&class_rtl); + + if (!ret) { + for (i = 0; rtl_attributes[i]; i ++) + sysdev_class_create_file(&class_rtl, rtl_attributes[i]); + } + return ret; +} + +static void rtl_teardown_sysfs(void) { + int i; + for (i = 0; rtl_attributes[i]; i ++) + sysdev_class_remove_file(&class_rtl, rtl_attributes[i]); + sysdev_class_unregister(&class_rtl); +} + +static int dmi_check_cb(const struct dmi_system_id *id) +{ + RTL_DEBUG("found IBM server '%s'\n", id->ident); + return 0; +} + +#define ibm_dmi_entry(NAME, TYPE) \ +{ \ + .ident = NAME, \ + .matches = { \ + DMI_MATCH(DMI_SYS_VENDOR, "IBM"), \ + DMI_MATCH(DMI_PRODUCT_NAME, TYPE), \ + }, \ + .callback = dmi_check_cb \ +} + +static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = { + ibm_dmi_entry("BladeCenter LS21", "7971"), + ibm_dmi_entry("BladeCenter LS22", "7901"), + ibm_dmi_entry("BladeCenter HS21 XM", "7995"), + ibm_dmi_entry("BladeCenter HS22", "7870"), + ibm_dmi_entry("BladeCenter HS22V", "7871"), + ibm_dmi_entry("System x3550 M2", "7946"), + ibm_dmi_entry("System x3650 M2", "7947"), + ibm_dmi_entry("System x3550 M3", "7944"), + ibm_dmi_entry("System x3650 M3", "7945"), + { } +}; + +static int __init ibm_rtl_init(void) { + unsigned long ebda_addr, ebda_size; + unsigned int ebda_kb; + int ret = -ENODEV, i; + + if (force) + pr_warning("ibm-rtl: module loaded by force\n"); + /* first ensure that we are running on IBM HW */ + else if (!dmi_check_system(ibm_rtl_dmi_table)) + return -ENODEV; + + /* Get the address for the Extended BIOS Data Area */ + ebda_addr = get_bios_ebda(); + if (!ebda_addr) { + RTL_DEBUG("no BIOS EBDA found\n"); + return -ENODEV; + } + + ebda_map = ioremap(ebda_addr, 4); + if (!ebda_map) + return -ENOMEM; + + /* First word in the EDBA is the Size in KB */ + ebda_kb = ioread16(ebda_map); + RTL_DEBUG("EBDA is %d kB\n", ebda_kb); + + if (ebda_kb == 0) + goto out; + + iounmap(ebda_map); + ebda_size = ebda_kb*1024; + + /* Remap the whole table */ + ebda_map = ioremap(ebda_addr, ebda_size); + if (!ebda_map) + return -ENOMEM; + + /* search for the _RTL_ signature at the start of the table */ + for (i = 0 ; i < ebda_size/sizeof(unsigned int); i++) { + struct ibm_rtl_table __iomem * tmp; + tmp = (struct ibm_rtl_table __iomem *) (ebda_map+i); + if ((readq(&tmp->signature) & RTL_MASK) == RTL_SIGNATURE) { + phys_addr_t addr; + unsigned int plen; + RTL_DEBUG("found RTL_SIGNATURE at %#llx\n", (u64)tmp); + rtl_table = tmp; + /* The address, value, width and offset are platform + * dependent and found in the ibm_rtl_table */ + rtl_cmd_width = ioread8(&rtl_table->cmd_granularity); + rtl_cmd_type = ioread8(&rtl_table->cmd_address_type); + RTL_DEBUG("rtl_cmd_width = %u, rtl_cmd_type = %u\n", + rtl_cmd_width, rtl_cmd_type); + addr = ioread32(&rtl_table->cmd_port_address); + RTL_DEBUG("addr = %#llx\n", addr); + plen = rtl_cmd_width/sizeof(char); + rtl_cmd_addr = rtl_port_map(addr, plen); + RTL_DEBUG("rtl_cmd_addr = %#llx\n", (u64)rtl_cmd_addr); + if (!rtl_cmd_addr) { + ret = -ENOMEM; + break; + } + ret = rtl_setup_sysfs(); + break; + } + } + +out: + if (ret) { + iounmap(ebda_map); + rtl_port_unmap(rtl_cmd_addr); + } + + return ret; +} + +static void __exit ibm_rtl_exit(void) +{ + if (rtl_table) { + RTL_DEBUG("cleaning up"); + /* do not leave the machine in SMI-free mode */ + ibm_rtl_write(0); + /* unmap, unlink and remove all traces */ + rtl_teardown_sysfs(); + iounmap(ebda_map); + rtl_port_unmap(rtl_cmd_addr); + } +} + +module_init(ibm_rtl_init); +module_exit(ibm_rtl_exit); -- cgit v1.2.3-70-g09d2 From d9027470b88631d0956ac37cdadfdeb9cdcf2c99 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 25 May 2010 12:31:38 -0700 Subject: [libata] Add ATA transport class This is a scheleton for libata transport class. All information is read only, exporting information from libata: - ata_port class: one per ATA port - ata_link class: one per ATA port or 15 for SATA Port Multiplier - ata_device class: up to 2 for PATA link, usually one for SATA. Signed-off-by: Gwendal Grignou Reviewed-by: Grant Grundler Signed-off-by: Jeff Garzik --- Documentation/ABI/testing/sysfs-ata | 99 +++++ drivers/ata/Makefile | 2 +- drivers/ata/libata-core.c | 49 ++- drivers/ata/libata-eh.c | 35 +- drivers/ata/libata-pmp.c | 18 +- drivers/ata/libata-scsi.c | 21 +- drivers/ata/libata-transport.c | 773 ++++++++++++++++++++++++++++++++++++ drivers/ata/libata-transport.h | 18 + drivers/ata/libata.h | 7 + include/linux/libata.h | 5 + 10 files changed, 987 insertions(+), 40 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-ata create mode 100644 drivers/ata/libata-transport.c create mode 100644 drivers/ata/libata-transport.h (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-ata b/Documentation/ABI/testing/sysfs-ata new file mode 100644 index 000000000000..0a932155cbba --- /dev/null +++ b/Documentation/ABI/testing/sysfs-ata @@ -0,0 +1,99 @@ +What: /sys/class/ata_... +Date: August 2008 +Contact: Gwendal Grignou +Description: + +Provide a place in sysfs for storing the ATA topology of the system. This allows +retrieving various information about ATA objects. + +Files under /sys/class/ata_port +------------------------------- + + For each port, a directory ataX is created where X is the ata_port_id of + the port. The device parent is the ata host device. + +idle_irq (read) + + Number of IRQ received by the port while idle [some ata HBA only]. + +nr_pmp_links (read) + + If a SATA Port Multiplier (PM) is connected, number of link behind it. + +Files under /sys/class/ata_link +------------------------------- + + Behind each port, there is a ata_link. If there is a SATA PM in the + topology, 15 ata_link objects are created. + + If a link is behind a port, the directory name is linkX, where X is + ata_port_id of the port. + If a link is behind a PM, its name is linkX.Y where X is ata_port_id + of the parent port and Y the PM port. + +hw_sata_spd_limit + + Maximum speed supported by the connected SATA device. + +sata_spd_limit + + Maximum speed imposed by libata. + +sata_spd + + Current speed of the link [1.5, 3Gps,...]. + +Files under /sys/class/ata_device +--------------------------------- + + Behind each link, up to two ata device are created. + The name of the directory is devX[.Y].Z where: + - X is ata_port_id of the port where the device is connected, + - Y the port of the PM if any, and + - Z the device id: for PATA, there is usually 2 devices [0,1], + only 1 for SATA. + +class + Device class. Can be "ata" for disk, "atapi" for packet device, + "pmp" for PM, or "none" if no device was found behind the link. + +dma_mode + + Transfer modes supported by the device when in DMA mode. + Mostly used by PATA device. + +pio_mode + + Transfer modes supported by the device when in PIO mode. + Mostly used by PATA device. + +xfer_mode + + Current transfer mode. + +id + + Cached result of IDENTIFY command, as described in ATA8 7.16 and 7.17. + Only valid if the device is not a PM. + +gscr + + Cached result of the dump of PM GSCR register. + Valid registers are: + 0: SATA_PMP_GSCR_PROD_ID, + 1: SATA_PMP_GSCR_REV, + 2: SATA_PMP_GSCR_PORT_INFO, + 32: SATA_PMP_GSCR_ERROR, + 33: SATA_PMP_GSCR_ERROR_EN, + 64: SATA_PMP_GSCR_FEAT, + 96: SATA_PMP_GSCR_FEAT_EN, + 130: SATA_PMP_GSCR_SII_GPIO + Only valid if the device is a PM. + +spdn_cnt + + Number of time libata decided to lower the speed of link due to errors. + +ering + + Formatted output of the error ring of the device. diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index d5df04a395ca..ccd461bb5ee4 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -99,7 +99,7 @@ obj-$(CONFIG_ATA_GENERIC) += ata_generic.o # Should be last libata driver obj-$(CONFIG_PATA_LEGACY) += pata_legacy.o -libata-objs := libata-core.o libata-scsi.o libata-eh.o +libata-objs := libata-core.o libata-scsi.o libata-eh.o libata-transport.o libata-$(CONFIG_ATA_SFF) += libata-sff.o libata-$(CONFIG_SATA_PMP) += libata-pmp.o libata-$(CONFIG_ATA_ACPI) += libata-acpi.o diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 932eaee50245..4012b33e8b8a 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -68,7 +68,7 @@ #include #include "libata.h" - +#include "libata-transport.h" /* debounce timing parameters in msecs { interval, duration, timeout } */ const unsigned long sata_deb_timing_normal[] = { 5, 100, 2000 }; @@ -1017,7 +1017,7 @@ const char *ata_mode_string(unsigned long xfer_mask) return ""; } -static const char *sata_spd_string(unsigned int spd) +const char *sata_spd_string(unsigned int spd) { static const char * const spd_str[] = { "1.5 Gbps", @@ -5517,7 +5517,8 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp) int i; /* clear everything except for devices */ - memset(link, 0, offsetof(struct ata_link, device[0])); + memset((void *)link + ATA_LINK_CLEAR_BEGIN, 0, + ATA_LINK_CLEAR_END - ATA_LINK_CLEAR_BEGIN); link->ap = ap; link->pmp = pmp; @@ -5591,7 +5592,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host) ap = kzalloc(sizeof(*ap), GFP_KERNEL); if (!ap) return NULL; - + ap->pflags |= ATA_PFLAG_INITIALIZING; ap->lock = &host->lock; ap->print_id = -1; @@ -6093,9 +6094,18 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) for (i = 0; i < host->n_ports; i++) host->ports[i]->print_id = ata_print_id++; + + /* Create associated sysfs transport objects */ + for (i = 0; i < host->n_ports; i++) { + rc = ata_tport_add(host->dev,host->ports[i]); + if (rc) { + goto err_tadd; + } + } + rc = ata_scsi_add_hosts(host, sht); if (rc) - return rc; + goto err_tadd; /* associate with ACPI nodes */ ata_acpi_associate(host); @@ -6136,6 +6146,13 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) } return 0; + + err_tadd: + while (--i >= 0) { + ata_tport_delete(host->ports[i]); + } + return rc; + } /** @@ -6226,6 +6243,13 @@ static void ata_port_detach(struct ata_port *ap) cancel_rearming_delayed_work(&ap->hotplug_task); skip_eh: + if (ap->pmp_link) { + int i; + for (i = 0; i < SATA_PMP_MAX_PORTS; i++) + ata_tlink_delete(&ap->pmp_link[i]); + } + ata_tport_delete(ap); + /* remove the associated SCSI host */ scsi_remove_host(ap->scsi_host); } @@ -6542,7 +6566,7 @@ static void __init ata_parse_force_param(void) static int __init ata_init(void) { - int rc = -ENOMEM; + int rc; ata_parse_force_param(); @@ -6552,12 +6576,25 @@ static int __init ata_init(void) return rc; } + libata_transport_init(); + ata_scsi_transport_template = ata_attach_transport(); + if (!ata_scsi_transport_template) { + ata_sff_exit(); + rc = -ENOMEM; + goto err_out; + } + printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n"); return 0; + +err_out: + return rc; } static void __exit ata_exit(void) { + ata_release_transport(ata_scsi_transport_template); + libata_transport_exit(); ata_sff_exit(); kfree(ata_force_tbl); } diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index e48302eae55f..95838b382231 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -57,6 +57,7 @@ enum { /* error flags */ ATA_EFLAG_IS_IO = (1 << 0), ATA_EFLAG_DUBIOUS_XFER = (1 << 1), + ATA_EFLAG_OLD_ER = (1 << 31), /* error categories */ ATA_ECAT_NONE = 0, @@ -396,14 +397,9 @@ static struct ata_ering_entry *ata_ering_top(struct ata_ering *ering) return NULL; } -static void ata_ering_clear(struct ata_ering *ering) -{ - memset(ering, 0, sizeof(*ering)); -} - -static int ata_ering_map(struct ata_ering *ering, - int (*map_fn)(struct ata_ering_entry *, void *), - void *arg) +int ata_ering_map(struct ata_ering *ering, + int (*map_fn)(struct ata_ering_entry *, void *), + void *arg) { int idx, rc = 0; struct ata_ering_entry *ent; @@ -422,6 +418,17 @@ static int ata_ering_map(struct ata_ering *ering, return rc; } +int ata_ering_clear_cb(struct ata_ering_entry *ent, void *void_arg) +{ + ent->eflags |= ATA_EFLAG_OLD_ER; + return 0; +} + +static void ata_ering_clear(struct ata_ering *ering) +{ + ata_ering_map(ering, ata_ering_clear_cb, NULL); +} + static unsigned int ata_eh_dev_action(struct ata_device *dev) { struct ata_eh_context *ehc = &dev->link->eh_context; @@ -572,19 +579,19 @@ void ata_scsi_error(struct Scsi_Host *host) int nr_timedout = 0; spin_lock_irqsave(ap->lock, flags); - + /* This must occur under the ap->lock as we don't want a polled recovery to race the real interrupt handler - + The lost_interrupt handler checks for any completed but non-notified command and completes much like an IRQ handler. - + We then fall into the error recovery code which will treat this as if normal completion won the race */ if (ap->ops->lost_interrupt) ap->ops->lost_interrupt(ap); - + list_for_each_entry_safe(scmd, tmp, &host->eh_cmd_q, eh_entry) { struct ata_queued_cmd *qc; @@ -628,7 +635,7 @@ void ata_scsi_error(struct Scsi_Host *host) ap->eh_tries = ATA_EH_MAX_TRIES; } else spin_unlock_wait(ap->lock); - + /* If we timed raced normal completion and there is nothing to recover nr_timedout == 0 why exactly are we doing error recovery ? */ @@ -1755,7 +1762,7 @@ static int speed_down_verdict_cb(struct ata_ering_entry *ent, void *void_arg) struct speed_down_verdict_arg *arg = void_arg; int cat; - if (ent->timestamp < arg->since) + if ((ent->eflags & ATA_EFLAG_OLD_ER) || (ent->timestamp < arg->since)) return -1; cat = ata_eh_categorize_error(ent->eflags, ent->err_mask, diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 224faabd7b7e..505470237d79 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -11,6 +11,7 @@ #include #include #include "libata.h" +#include "libata-transport.h" const struct ata_port_operations sata_pmp_port_ops = { .inherits = &sata_port_ops, @@ -312,10 +313,10 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info) return rc; } -static int sata_pmp_init_links(struct ata_port *ap, int nr_ports) +static int sata_pmp_init_links (struct ata_port *ap, int nr_ports) { struct ata_link *pmp_link = ap->pmp_link; - int i; + int i, err; if (!pmp_link) { pmp_link = kzalloc(sizeof(pmp_link[0]) * SATA_PMP_MAX_PORTS, @@ -327,6 +328,13 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports) ata_link_init(ap, &pmp_link[i], i); ap->pmp_link = pmp_link; + + for (i = 0; i < SATA_PMP_MAX_PORTS; i++) { + err = ata_tlink_add(&pmp_link[i]); + if (err) { + goto err_tlink; + } + } } for (i = 0; i < nr_ports; i++) { @@ -339,6 +347,12 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports) } return 0; + err_tlink: + while (--i >= 0) + ata_tlink_delete(&pmp_link[i]); + kfree(pmp_link); + ap->pmp_link = NULL; + return err; } static void sata_pmp_quirks(struct ata_port *ap) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index a89172c100f5..c16f5c151735 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -51,6 +51,7 @@ #include #include "libata.h" +#include "libata-transport.h" #define SECTOR_SIZE 512 #define ATA_SCSI_RBUF_SIZE 4096 @@ -64,9 +65,6 @@ static struct ata_device *__ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev); static struct ata_device *ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev); -static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, - unsigned int id, unsigned int lun); - #define RW_RECOVERY_MPAGE 0x1 #define RW_RECOVERY_MPAGE_LEN 12 @@ -106,17 +104,6 @@ static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = { 0, 30 /* extended self test time, see 05-359r1 */ }; -/* - * libata transport template. libata doesn't do real transport stuff. - * It just needs the eh_timed_out hook. - */ -static struct scsi_transport_template ata_scsi_transport_template = { - .eh_strategy_handler = ata_scsi_error, - .eh_timed_out = ata_scsi_timed_out, - .user_scan = ata_scsi_user_scan, -}; - - static const struct { enum link_pm value; const char *name; @@ -3334,7 +3321,7 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht) *(struct ata_port **)&shost->hostdata[0] = ap; ap->scsi_host = shost; - shost->transportt = &ata_scsi_transport_template; + shost->transportt = ata_scsi_transport_template; shost->unique_id = ap->print_id; shost->max_id = 16; shost->max_lun = 1; @@ -3616,8 +3603,8 @@ void ata_scsi_hotplug(struct work_struct *work) * RETURNS: * Zero. */ -static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, - unsigned int id, unsigned int lun) +int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, + unsigned int id, unsigned int lun) { struct ata_port *ap = ata_shost_to_port(shost); unsigned long flags; diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c new file mode 100644 index 000000000000..a80860f537e4 --- /dev/null +++ b/drivers/ata/libata-transport.c @@ -0,0 +1,773 @@ +/* + * Copyright 2008 ioogle, Inc. All rights reserved. + * Released under GPL v2. + * + * Libata transport class. + * + * The ATA transport class contains common code to deal with ATA HBAs, + * an approximated representation of ATA topologies in the driver model, + * and various sysfs attributes to expose these topologies and management + * interfaces to user-space. + * + * There are 3 objects defined in in this class: + * - ata_port + * - ata_link + * - ata_device + * Each port has a link object. Each link can have up to two devices for PATA + * and generally one for SATA. + * If there is SATA port multiplier [PMP], 15 additional ata_link object are + * created. + * + * These objects are created when the ata host is initialized and when a PMP is + * found. They are removed only when the HBA is removed, cleaned before the + * error handler runs. + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "libata.h" +#include "libata-transport.h" + +#define ATA_PORT_ATTRS 2 +#define ATA_LINK_ATTRS 3 +#define ATA_DEV_ATTRS 9 + +struct scsi_transport_template; +struct scsi_transport_template *ata_scsi_transport_template; + +struct ata_internal { + struct scsi_transport_template t; + + struct device_attribute private_port_attrs[ATA_PORT_ATTRS]; + struct device_attribute private_link_attrs[ATA_LINK_ATTRS]; + struct device_attribute private_dev_attrs[ATA_DEV_ATTRS]; + + struct transport_container link_attr_cont; + struct transport_container dev_attr_cont; + + /* + * The array of null terminated pointers to attributes + * needed by scsi_sysfs.c + */ + struct device_attribute *link_attrs[ATA_LINK_ATTRS + 1]; + struct device_attribute *port_attrs[ATA_PORT_ATTRS + 1]; + struct device_attribute *dev_attrs[ATA_DEV_ATTRS + 1]; +}; +#define to_ata_internal(tmpl) container_of(tmpl, struct ata_internal, t) + + +#define tdev_to_device(d) \ + container_of((d), struct ata_device, tdev) +#define transport_class_to_dev(dev) \ + tdev_to_device((dev)->parent) + +#define tdev_to_link(d) \ + container_of((d), struct ata_link, tdev) +#define transport_class_to_link(dev) \ + tdev_to_link((dev)->parent) + +#define tdev_to_port(d) \ + container_of((d), struct ata_port, tdev) +#define transport_class_to_port(dev) \ + tdev_to_port((dev)->parent) + + +/* Device objects are always created whit link objects */ +static int ata_tdev_add(struct ata_device *dev); +static void ata_tdev_delete(struct ata_device *dev); + + +/* + * Hack to allow attributes of the same name in different objects. + */ +#define ATA_DEVICE_ATTR(_prefix,_name,_mode,_show,_store) \ + struct device_attribute device_attr_##_prefix##_##_name = \ + __ATTR(_name,_mode,_show,_store) + +#define ata_bitfield_name_match(title, table) \ +static ssize_t \ +get_ata_##title##_names(u32 table_key, char *buf) \ +{ \ + char *prefix = ""; \ + ssize_t len = 0; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(table); i++) { \ + if (table[i].value & table_key) { \ + len += sprintf(buf + len, "%s%s", \ + prefix, table[i].name); \ + prefix = ", "; \ + } \ + } \ + len += sprintf(buf + len, "\n"); \ + return len; \ +} + +#define ata_bitfield_name_search(title, table) \ +static ssize_t \ +get_ata_##title##_names(u32 table_key, char *buf) \ +{ \ + ssize_t len = 0; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(table); i++) { \ + if (table[i].value == table_key) { \ + len += sprintf(buf + len, "%s", \ + table[i].name); \ + break; \ + } \ + } \ + len += sprintf(buf + len, "\n"); \ + return len; \ +} + +static struct { + u32 value; + char *name; +} ata_class_names[] = { + { ATA_DEV_UNKNOWN, "unknown" }, + { ATA_DEV_ATA, "ata" }, + { ATA_DEV_ATA_UNSUP, "ata" }, + { ATA_DEV_ATAPI, "atapi" }, + { ATA_DEV_ATAPI_UNSUP, "atapi" }, + { ATA_DEV_PMP, "pmp" }, + { ATA_DEV_PMP_UNSUP, "pmp" }, + { ATA_DEV_SEMB, "semb" }, + { ATA_DEV_SEMB_UNSUP, "semb" }, + { ATA_DEV_NONE, "none" } +}; +ata_bitfield_name_search(class, ata_class_names) + + +static struct { + u32 value; + char *name; +} ata_err_names[] = { + { AC_ERR_DEV, "DeviceError" }, + { AC_ERR_HSM, "HostStateMachineError" }, + { AC_ERR_TIMEOUT, "Timeout" }, + { AC_ERR_MEDIA, "MediaError" }, + { AC_ERR_ATA_BUS, "BusError" }, + { AC_ERR_HOST_BUS, "HostBusError" }, + { AC_ERR_SYSTEM, "SystemError" }, + { AC_ERR_INVALID, "InvalidArg" }, + { AC_ERR_OTHER, "Unknown" }, + { AC_ERR_NODEV_HINT, "NoDeviceHint" }, + { AC_ERR_NCQ, "NCQError" } +}; +ata_bitfield_name_match(err, ata_err_names) + +static struct { + u32 value; + char *name; +} ata_xfer_names[] = { + { XFER_UDMA_7, "XFER_UDMA_7" }, + { XFER_UDMA_6, "XFER_UDMA_6" }, + { XFER_UDMA_5, "XFER_UDMA_5" }, + { XFER_UDMA_4, "XFER_UDMA_4" }, + { XFER_UDMA_3, "XFER_UDMA_3" }, + { XFER_UDMA_2, "XFER_UDMA_2" }, + { XFER_UDMA_1, "XFER_UDMA_1" }, + { XFER_UDMA_0, "XFER_UDMA_0" }, + { XFER_MW_DMA_4, "XFER_MW_DMA_4" }, + { XFER_MW_DMA_3, "XFER_MW_DMA_3" }, + { XFER_MW_DMA_2, "XFER_MW_DMA_2" }, + { XFER_MW_DMA_1, "XFER_MW_DMA_1" }, + { XFER_MW_DMA_0, "XFER_MW_DMA_0" }, + { XFER_SW_DMA_2, "XFER_SW_DMA_2" }, + { XFER_SW_DMA_1, "XFER_SW_DMA_1" }, + { XFER_SW_DMA_0, "XFER_SW_DMA_0" }, + { XFER_PIO_6, "XFER_PIO_6" }, + { XFER_PIO_5, "XFER_PIO_5" }, + { XFER_PIO_4, "XFER_PIO_4" }, + { XFER_PIO_3, "XFER_PIO_3" }, + { XFER_PIO_2, "XFER_PIO_2" }, + { XFER_PIO_1, "XFER_PIO_1" }, + { XFER_PIO_0, "XFER_PIO_0" }, + { XFER_PIO_SLOW, "XFER_PIO_SLOW" } +}; +ata_bitfield_name_match(xfer,ata_xfer_names) + +/* + * ATA Port attributes + */ +#define ata_port_show_simple(field, name, format_string, cast) \ +static ssize_t \ +show_ata_port_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct ata_port *ap = transport_class_to_port(dev); \ + \ + return snprintf(buf, 20, format_string, cast ap->field); \ +} + +#define ata_port_simple_attr(field, name, format_string, type) \ + ata_port_show_simple(field, name, format_string, (type)) \ +static DEVICE_ATTR(name, S_IRUGO, show_ata_port_##name, NULL) + +ata_port_simple_attr(nr_pmp_links, nr_pmp_links, "%d\n", int); +ata_port_simple_attr(stats.idle_irq, idle_irq, "%ld\n", unsigned long); + +static DECLARE_TRANSPORT_CLASS(ata_port_class, + "ata_port", NULL, NULL, NULL); + +static void ata_tport_release(struct device *dev) +{ + put_device(dev->parent); +} + +/** + * ata_is_port -- check if a struct device represents a ATA port + * @dev: device to check + * + * Returns: + * %1 if the device represents a ATA Port, %0 else + */ +int ata_is_port(const struct device *dev) +{ + return dev->release == ata_tport_release; +} + +static int ata_tport_match(struct attribute_container *cont, + struct device *dev) +{ + if (!ata_is_port(dev)) + return 0; + return &ata_scsi_transport_template->host_attrs.ac == cont; +} + +/** + * ata_tport_delete -- remove ATA PORT + * @port: ATA PORT to remove + * + * Removes the specified ATA PORT. Remove the associated link as well. + */ +void ata_tport_delete(struct ata_port *ap) +{ + struct device *dev = &ap->tdev; + + ata_tlink_delete(&ap->link); + + transport_remove_device(dev); + device_del(dev); + transport_destroy_device(dev); + put_device(dev); +} + +/** ata_tport_add - initialize a transport ATA port structure + * + * @parent: parent device + * @ap: existing ata_port structure + * + * Initialize a ATA port structure for sysfs. It will be added to the device + * tree below the device specified by @parent which could be a PCI device. + * + * Returns %0 on success + */ +int ata_tport_add(struct device *parent, + struct ata_port *ap) +{ + int error; + struct device *dev = &ap->tdev; + + device_initialize(dev); + + dev->parent = get_device(parent); + dev->release = ata_tport_release; + dev_set_name(dev, "ata%d", ap->print_id); + transport_setup_device(dev); + error = device_add(dev); + if (error) { + goto tport_err; + } + + transport_add_device(dev); + transport_configure_device(dev); + + error = ata_tlink_add(&ap->link); + if (error) { + goto tport_link_err; + } + return 0; + + tport_link_err: + transport_remove_device(dev); + device_del(dev); + + tport_err: + transport_destroy_device(dev); + put_device(dev); + return error; +} + + +/* + * ATA link attributes + */ + + +#define ata_link_show_linkspeed(field) \ +static ssize_t \ +show_ata_link_##field(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct ata_link *link = transport_class_to_link(dev); \ + \ + return sprintf(buf,"%s\n", sata_spd_string(fls(link->field))); \ +} + +#define ata_link_linkspeed_attr(field) \ + ata_link_show_linkspeed(field) \ +static DEVICE_ATTR(field, S_IRUGO, show_ata_link_##field, NULL) + +ata_link_linkspeed_attr(hw_sata_spd_limit); +ata_link_linkspeed_attr(sata_spd_limit); +ata_link_linkspeed_attr(sata_spd); + + +static DECLARE_TRANSPORT_CLASS(ata_link_class, + "ata_link", NULL, NULL, NULL); + +static void ata_tlink_release(struct device *dev) +{ + put_device(dev->parent); +} + +/** + * ata_is_link -- check if a struct device represents a ATA link + * @dev: device to check + * + * Returns: + * %1 if the device represents a ATA link, %0 else + */ +int ata_is_link(const struct device *dev) +{ + return dev->release == ata_tlink_release; +} + +static int ata_tlink_match(struct attribute_container *cont, + struct device *dev) +{ + struct ata_internal* i = to_ata_internal(ata_scsi_transport_template); + if (!ata_is_link(dev)) + return 0; + return &i->link_attr_cont.ac == cont; +} + +/** + * ata_tlink_delete -- remove ATA LINK + * @port: ATA LINK to remove + * + * Removes the specified ATA LINK. remove associated ATA device(s) as well. + */ +void ata_tlink_delete(struct ata_link *link) +{ + struct device *dev = &link->tdev; + struct ata_device *ata_dev; + + ata_for_each_dev(ata_dev, link, ALL) { + ata_tdev_delete(ata_dev); + } + + transport_remove_device(dev); + device_del(dev); + transport_destroy_device(dev); + put_device(dev); +} + +/** + * ata_tlink_add -- initialize a transport ATA link structure + * @link: allocated ata_link structure. + * + * Initialize an ATA LINK structure for sysfs. It will be added in the + * device tree below the ATA PORT it belongs to. + * + * Returns %0 on success + */ +int ata_tlink_add(struct ata_link *link) +{ + struct device *dev = &link->tdev; + struct ata_port *ap = link->ap; + struct ata_device *ata_dev; + int error; + + device_initialize(dev); + dev->parent = get_device(&ap->tdev); + dev->release = ata_tlink_release; + if (ata_is_host_link(link)) + dev_set_name(dev, "link%d", ap->print_id); + else + dev_set_name(dev, "link%d.%d", ap->print_id, link->pmp); + + transport_setup_device(dev); + + error = device_add(dev); + if (error) { + goto tlink_err; + } + + transport_add_device(dev); + transport_configure_device(dev); + + ata_for_each_dev(ata_dev, link, ALL) { + error = ata_tdev_add(ata_dev); + if (error) { + goto tlink_dev_err; + } + } + return 0; + tlink_dev_err: + while (--ata_dev >= link->device) { + ata_tdev_delete(ata_dev); + } + transport_remove_device(dev); + device_del(dev); + tlink_err: + transport_destroy_device(dev); + put_device(dev); + return error; +} + +/* + * ATA device attributes + */ + +#define ata_dev_show_class(title, field) \ +static ssize_t \ +show_ata_dev_##field(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct ata_device *ata_dev = transport_class_to_dev(dev); \ + \ + return get_ata_##title##_names(ata_dev->field, buf); \ +} + +#define ata_dev_attr(title, field) \ + ata_dev_show_class(title, field) \ +static DEVICE_ATTR(field, S_IRUGO, show_ata_dev_##field, NULL) + +ata_dev_attr(class, class); +ata_dev_attr(xfer, pio_mode); +ata_dev_attr(xfer, dma_mode); +ata_dev_attr(xfer, xfer_mode); + + +#define ata_dev_show_simple(field, format_string, cast) \ +static ssize_t \ +show_ata_dev_##field(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct ata_device *ata_dev = transport_class_to_dev(dev); \ + \ + return snprintf(buf, 20, format_string, cast ata_dev->field); \ +} + +#define ata_dev_simple_attr(field, format_string, type) \ + ata_dev_show_simple(field, format_string, (type)) \ +static DEVICE_ATTR(field, S_IRUGO, \ + show_ata_dev_##field, NULL) + +ata_dev_simple_attr(spdn_cnt, "%d\n", int); + +struct ata_show_ering_arg { + char* buf; + int written; +}; + +static int ata_show_ering(struct ata_ering_entry *ent, void *void_arg) +{ + struct ata_show_ering_arg* arg = void_arg; + struct timespec time; + + jiffies_to_timespec(ent->timestamp,&time); + arg->written += sprintf(arg->buf + arg->written, + "[%5lu.%06lu]", + time.tv_sec, time.tv_nsec); + arg->written += get_ata_err_names(ent->err_mask, + arg->buf + arg->written); + return 0; +} + +static ssize_t +show_ata_dev_ering(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ata_device *ata_dev = transport_class_to_dev(dev); + struct ata_show_ering_arg arg = { buf, 0 }; + + ata_ering_map(&ata_dev->ering, ata_show_ering, &arg); + return arg.written; +} + + +static DEVICE_ATTR(ering, S_IRUGO, show_ata_dev_ering, NULL); + +static ssize_t +show_ata_dev_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ata_device *ata_dev = transport_class_to_dev(dev); + int written = 0, i = 0; + + if (ata_dev->class == ATA_DEV_PMP) + return 0; + for(i=0;iid[i], + ((i+1) & 7) ? ' ' : '\n'); + } + return written; +} + +static DEVICE_ATTR(id, S_IRUGO, show_ata_dev_id, NULL); + +static ssize_t +show_ata_dev_gscr(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ata_device *ata_dev = transport_class_to_dev(dev); + int written = 0, i = 0; + + if (ata_dev->class != ATA_DEV_PMP) + return 0; + for(i=0;igscr[i], + ((i+1) & 3) ? ' ' : '\n'); + } + if (SATA_PMP_GSCR_DWORDS & 3) + buf[written-1] = '\n'; + return written; +} + +static DEVICE_ATTR(gscr, S_IRUGO, show_ata_dev_gscr, NULL); + +static DECLARE_TRANSPORT_CLASS(ata_dev_class, + "ata_device", NULL, NULL, NULL); + +static void ata_tdev_release(struct device *dev) +{ + put_device(dev->parent); +} + +/** + * ata_is_ata_dev -- check if a struct device represents a ATA device + * @dev: device to check + * + * Returns: + * %1 if the device represents a ATA device, %0 else + */ +int ata_is_ata_dev(const struct device *dev) +{ + return dev->release == ata_tdev_release; +} + +static int ata_tdev_match(struct attribute_container *cont, + struct device *dev) +{ + struct ata_internal* i = to_ata_internal(ata_scsi_transport_template); + if (!ata_is_ata_dev(dev)) + return 0; + return &i->dev_attr_cont.ac == cont; +} + +/** + * ata_tdev_free -- free a ATA LINK + * @dev: ATA PHY to free + * + * Frees the specified ATA PHY. + * + * Note: + * This function must only be called on a PHY that has not + * successfully been added using ata_tdev_add(). + */ +static void ata_tdev_free(struct ata_device *dev) +{ + transport_destroy_device(&dev->tdev); + put_device(&dev->tdev); +} + +/** + * ata_tdev_delete -- remove ATA device + * @port: ATA PORT to remove + * + * Removes the specified ATA device. + */ +static void ata_tdev_delete(struct ata_device *ata_dev) +{ + struct device *dev = &ata_dev->tdev; + + transport_remove_device(dev); + device_del(dev); + ata_tdev_free(ata_dev); +} + + +/** + * ata_tdev_add -- initialize a transport ATA device structure. + * @ata_dev: ata_dev structure. + * + * Initialize an ATA device structure for sysfs. It will be added in the + * device tree below the ATA LINK device it belongs to. + * + * Returns %0 on success + */ +static int ata_tdev_add(struct ata_device *ata_dev) +{ + struct device *dev = &ata_dev->tdev; + struct ata_link *link = ata_dev->link; + struct ata_port *ap = link->ap; + int error; + + device_initialize(dev); + dev->parent = get_device(&link->tdev); + dev->release = ata_tdev_release; + if (ata_is_host_link(link)) + dev_set_name(dev, "dev%d.%d", ap->print_id,ata_dev->devno); + else + dev_set_name(dev, "dev%d.%d.0", ap->print_id, link->pmp); + + transport_setup_device(dev); + error = device_add(dev); + if (error) { + ata_tdev_free(ata_dev); + return error; + } + + transport_add_device(dev); + transport_configure_device(dev); + return 0; +} + + +/* + * Setup / Teardown code + */ + +#define SETUP_TEMPLATE(attrb, field, perm, test) \ + i->private_##attrb[count] = dev_attr_##field; \ + i->private_##attrb[count].attr.mode = perm; \ + i->attrb[count] = &i->private_##attrb[count]; \ + if (test) \ + count++ + +#define SETUP_LINK_ATTRIBUTE(field) \ + SETUP_TEMPLATE(link_attrs, field, S_IRUGO, 1) + +#define SETUP_PORT_ATTRIBUTE(field) \ + SETUP_TEMPLATE(port_attrs, field, S_IRUGO, 1) + +#define SETUP_DEV_ATTRIBUTE(field) \ + SETUP_TEMPLATE(dev_attrs, field, S_IRUGO, 1) + +/** + * ata_attach_transport -- instantiate ATA transport template + */ +struct scsi_transport_template *ata_attach_transport(void) +{ + struct ata_internal *i; + int count; + + i = kzalloc(sizeof(struct ata_internal), GFP_KERNEL); + if (!i) + return NULL; + + i->t.eh_strategy_handler = ata_scsi_error; + i->t.eh_timed_out = ata_scsi_timed_out; + i->t.user_scan = ata_scsi_user_scan; + + i->t.host_attrs.ac.attrs = &i->port_attrs[0]; + i->t.host_attrs.ac.class = &ata_port_class.class; + i->t.host_attrs.ac.match = ata_tport_match; + transport_container_register(&i->t.host_attrs); + + i->link_attr_cont.ac.class = &ata_link_class.class; + i->link_attr_cont.ac.attrs = &i->link_attrs[0]; + i->link_attr_cont.ac.match = ata_tlink_match; + transport_container_register(&i->link_attr_cont); + + i->dev_attr_cont.ac.class = &ata_dev_class.class; + i->dev_attr_cont.ac.attrs = &i->dev_attrs[0]; + i->dev_attr_cont.ac.match = ata_tdev_match; + transport_container_register(&i->dev_attr_cont); + + count = 0; + SETUP_PORT_ATTRIBUTE(nr_pmp_links); + SETUP_PORT_ATTRIBUTE(idle_irq); + BUG_ON(count > ATA_PORT_ATTRS); + i->port_attrs[count] = NULL; + + count = 0; + SETUP_LINK_ATTRIBUTE(hw_sata_spd_limit); + SETUP_LINK_ATTRIBUTE(sata_spd_limit); + SETUP_LINK_ATTRIBUTE(sata_spd); + BUG_ON(count > ATA_LINK_ATTRS); + i->link_attrs[count] = NULL; + + count = 0; + SETUP_DEV_ATTRIBUTE(class); + SETUP_DEV_ATTRIBUTE(pio_mode); + SETUP_DEV_ATTRIBUTE(dma_mode); + SETUP_DEV_ATTRIBUTE(xfer_mode); + SETUP_DEV_ATTRIBUTE(spdn_cnt); + SETUP_DEV_ATTRIBUTE(ering); + SETUP_DEV_ATTRIBUTE(id); + SETUP_DEV_ATTRIBUTE(gscr); + BUG_ON(count > ATA_DEV_ATTRS); + i->dev_attrs[count] = NULL; + + return &i->t; +} + +/** + * ata_release_transport -- release ATA transport template instance + * @t: transport template instance + */ +void ata_release_transport(struct scsi_transport_template *t) +{ + struct ata_internal *i = to_ata_internal(t); + + transport_container_unregister(&i->t.host_attrs); + transport_container_unregister(&i->link_attr_cont); + transport_container_unregister(&i->dev_attr_cont); + + kfree(i); +} + +__init int libata_transport_init(void) +{ + int error; + + error = transport_class_register(&ata_link_class); + if (error) + goto out_unregister_transport; + error = transport_class_register(&ata_port_class); + if (error) + goto out_unregister_link; + error = transport_class_register(&ata_dev_class); + if (error) + goto out_unregister_port; + return 0; + + out_unregister_port: + transport_class_unregister(&ata_port_class); + out_unregister_link: + transport_class_unregister(&ata_link_class); + out_unregister_transport: + return error; + +} + +void __exit libata_transport_exit(void) +{ + transport_class_unregister(&ata_link_class); + transport_class_unregister(&ata_port_class); + transport_class_unregister(&ata_dev_class); +} diff --git a/drivers/ata/libata-transport.h b/drivers/ata/libata-transport.h new file mode 100644 index 000000000000..2820cf864f11 --- /dev/null +++ b/drivers/ata/libata-transport.h @@ -0,0 +1,18 @@ +#ifndef _LIBATA_TRANSPORT_H +#define _LIBATA_TRANSPORT_H + + +extern struct scsi_transport_template *ata_scsi_transport_template; + +int ata_tlink_add(struct ata_link *link); +void ata_tlink_delete(struct ata_link *link); + +int ata_tport_add(struct device *parent, struct ata_port *ap); +void ata_tport_delete(struct ata_port *ap); + +struct scsi_transport_template *ata_attach_transport(void); +void ata_release_transport(struct scsi_transport_template *t); + +__init int libata_transport_init(void); +void __exit libata_transport_exit(void); +#endif diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 9ce1ecc63e39..142102b94df5 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -102,6 +102,7 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); extern struct ata_port *ata_port_alloc(struct ata_host *host); extern void ata_dev_enable_pm(struct ata_device *dev, enum link_pm policy); extern void ata_lpm_schedule(struct ata_port *ap, enum link_pm); +extern const char *sata_spd_string(unsigned int spd); /* libata-acpi.c */ #ifdef CONFIG_ATA_ACPI @@ -137,6 +138,9 @@ extern void ata_scsi_hotplug(struct work_struct *work); extern void ata_schedule_scsi_eh(struct Scsi_Host *shost); extern void ata_scsi_dev_rescan(struct work_struct *work); extern int ata_bus_probe(struct ata_port *ap); +extern int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, + unsigned int id, unsigned int lun); + /* libata-eh.c */ extern unsigned long ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd); @@ -164,6 +168,9 @@ extern int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, ata_postreset_fn_t postreset, struct ata_link **r_failed_disk); extern void ata_eh_finish(struct ata_port *ap); +extern int ata_ering_map(struct ata_ering *ering, + int (*map_fn)(struct ata_ering_entry *, void *), + void *arg); /* libata-pmp.c */ #ifdef CONFIG_SATA_PMP diff --git a/include/linux/libata.h b/include/linux/libata.h index 45fb2967b66d..c50f66d4382c 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -604,6 +604,7 @@ struct ata_device { union acpi_object *gtf_cache; unsigned int gtf_filter; #endif + struct device tdev; /* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */ u64 n_sectors; /* size of device, if ATA */ u64 n_native_sectors; /* native size, if ATA */ @@ -690,6 +691,7 @@ struct ata_link { struct ata_port *ap; int pmp; /* port multiplier port # */ + struct device tdev; unsigned int active_tag; /* active tag on this link */ u32 sactive; /* active NCQ commands */ @@ -707,6 +709,8 @@ struct ata_link { struct ata_device device[ATA_MAX_DEVICES]; }; +#define ATA_LINK_CLEAR_BEGIN offsetof(struct ata_link, active_tag) +#define ATA_LINK_CLEAR_END offsetof(struct ata_link, device[0]) struct ata_port { struct Scsi_Host *scsi_host; /* our co-allocated scsi host */ @@ -752,6 +756,7 @@ struct ata_port { struct ata_port_stats stats; struct ata_host *host; struct device *dev; + struct device tdev; struct mutex scsi_scan_mutex; struct delayed_work hotplug_task; -- cgit v1.2.3-70-g09d2 From a648ec05bb950fae2f35d0490ddd6cf15010af72 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Fri, 6 Aug 2010 16:11:02 +0200 Subject: Dynamic Debug: Introduce ddebug_query= boot parameter Dynamic debug lacks the ability to enable debug messages at boot time. One could patch initramfs or service startup scripts to write to /sys/../dynamic_debug/control, but this sucks. This patch makes it possible to pass a query in the same format one can write to /sys/../dynamic_debug/control via boot param. When dynamic debug gets initialized, this query will automatically be applied. Signed-off-by: Thomas Renninger Acked-by: jbaron@redhat.com Acked-by: Pekka Enberg Signed-off-by: Greg Kroah-Hartman --- Documentation/dynamic-debug-howto.txt | 22 +++++++++++++++++++++- Documentation/kernel-parameters.txt | 7 ++++++- lib/dynamic_debug.c | 25 +++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/dynamic-debug-howto.txt b/Documentation/dynamic-debug-howto.txt index 674c5663d346..58ea64a96165 100644 --- a/Documentation/dynamic-debug-howto.txt +++ b/Documentation/dynamic-debug-howto.txt @@ -24,7 +24,7 @@ Dynamic debug has even more useful features: read to display the complete list of known debug statements, to help guide you Controlling dynamic debug Behaviour -=============================== +=================================== The behaviour of pr_debug()/dev_debug()s are controlled via writing to a control file in the 'debugfs' filesystem. Thus, you must first mount the debugfs @@ -212,6 +212,26 @@ Note the regexp ^[-+=][scp]+$ matches a flags specification. Note also that there is no convenient syntax to remove all the flags at once, you need to use "-psc". + +Debug messages during boot process +================================== + +To be able to activate debug messages during the boot process, +even before userspace and debugfs exists, use the boot parameter: +ddebug_query="QUERY" + +QUERY follows the syntax described above, but must not exceed 1023 +characters. The enablement of debug messages is done as an arch_initcall. +Thus you can enable debug messages in all code processed after this +arch_initcall via this boot parameter. +On an x86 system for example ACPI enablement is a subsys_initcall and +ddebug_query="file ec.c +p" +will show early Embedded Controller transactions during ACPI setup if +your machine (typically a laptop) has an Embedded Controller. +PCI (or other devices) initialization also is a hot candidate for using +this boot parameter for debugging purposes. + + Examples ======== diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 8dd7248508a9..3d854c9ae53a 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -43,10 +43,11 @@ parameter is applicable: AVR32 AVR32 architecture is enabled. AX25 Appropriate AX.25 support is enabled. BLACKFIN Blackfin architecture is enabled. - DRM Direct Rendering Management support is enabled. EDD BIOS Enhanced Disk Drive Services (EDD) is enabled EFI EFI Partitioning (GPT) is enabled EIDE EIDE/ATAPI support is enabled. + DRM Direct Rendering Management support is enabled. + DYNAMIC_DEBUG Build in debug messages and enable them at runtime FB The frame buffer device is enabled. GCOV GCOV profiling is enabled. HW Appropriate hardware is enabled. @@ -570,6 +571,10 @@ and is between 256 and 4096 characters. It is defined in the file Format: , See also Documentation/input/joystick-parport.txt + ddebug_query= [KNL,DYNAMIC_DEBUG] Enable debug messages at early boot + time. See Documentation/dynamic-debug-howto.txt for + details. + debug [KNL] Enable kernel debugging (events log level). debug_locks_verbose= diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 84d103c474e4..44ce66bdb211 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -450,6 +450,19 @@ static int ddebug_exec_query(char *query_string) return 0; } +static __initdata char ddebug_setup_string[1024]; +static __init int ddebug_setup_query(char *str) +{ + if (strlen(str) >= 1024) { + pr_warning("ddebug boot param string too large\n"); + return 0; + } + strcpy(ddebug_setup_string, str); + return 1; +} + +__setup("ddebug_query=", ddebug_setup_query); + /* * File_ops->write method for /dynamic_debug/conrol. Gathers the * command text from userspace, parses and executes it. @@ -769,6 +782,18 @@ static int __init dynamic_debug_init(void) } ret = ddebug_add_module(iter_start, n, modname); } + + /* ddebug_query boot param got passed -> set it up */ + if (ddebug_setup_string[0] != '\0') { + ret = ddebug_exec_query(ddebug_setup_string); + if (ret) + pr_warning("Invalid ddebug boot param %s", + ddebug_setup_string); + else + pr_info("ddebug initialized with string %s", + ddebug_setup_string); + } + out_free: if (ret) { ddebug_remove_all_tables(); -- cgit v1.2.3-70-g09d2 From cf4ece53460c64a04a643ef13f6b6cb4bf3a8342 Mon Sep 17 00:00:00 2001 From: Masayuki Ohtak Date: Wed, 1 Sep 2010 21:16:30 +0900 Subject: add Packet hub driver for Topcliff Platform controller hub Packet hub driver of Topcliff PCH Topcliff PCH is the platform controller hub that is going to be used in Intel's upcoming general embedded platform. All IO peripherals in Topcliff PCH are actually devices sitting on AMBA bus. Packet hub is a special converter device in Topcliff PCH that translate AMBA transactions to PCI Express transactions and vice versa. Thus packet hub helps present all IO peripherals in Topcliff PCH as PCIE devices to IA system. Topcliff PCH has MAC address and Option ROM data. These data are in SROM which is connected to PCIE bus. Packet hub driver of Topcliff PCH can access MAC address and Option ROM data in SROM via sysfs interface. Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-module | 12 + drivers/misc/Kconfig | 12 + drivers/misc/Makefile | 1 + drivers/misc/pch_phub.c | 717 +++++++++++++++++++++++++++++++++ 4 files changed, 742 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-module create mode 100644 drivers/misc/pch_phub.c (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-module b/Documentation/ABI/testing/sysfs-module new file mode 100644 index 000000000000..cfcec3bffc0a --- /dev/null +++ b/Documentation/ABI/testing/sysfs-module @@ -0,0 +1,12 @@ +What: /sys/module/pch_phub/drivers/.../pch_mac +Date: August 2010 +KernelVersion: 2.6.35 +Contact: masa-korg@dsn.okisemi.com +Description: Write/read GbE MAC address. + +What: /sys/module/pch_phub/drivers/.../pch_firmware +Date: August 2010 +KernelVersion: 2.6.35 +Contact: masa-korg@dsn.okisemi.com +Description: Write/read Option ROM data. + diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index b74331260744..6f46e27ed682 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -390,6 +390,18 @@ config BMP085 To compile this driver as a module, choose M here: the module will be called bmp085. +config PCH_PHUB + tristate "PCH Packet Hub of Intel Topcliff" + depends on PCI + help + This driver is for PCH(Platform controller Hub) PHUB(Packet Hub) of + Intel Topcliff which is an IOH(Input/Output Hub) for x86 embedded + processor. The Topcliff has MAC address and Option ROM data in SROM. + This driver can access MAC address and Option ROM data in SROM. + + To compile this driver as a module, choose M here: the module will + be called pch_phub. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 42eab95cde2a..9f2986b4da2f 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -35,3 +35,4 @@ obj-y += eeprom/ obj-y += cb710/ obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o +obj-$(CONFIG_PCH_PHUB) += pch_phub.o diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c new file mode 100644 index 000000000000..ed56e4f91fa0 --- /dev/null +++ b/drivers/misc/pch_phub.c @@ -0,0 +1,717 @@ +/* + * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PHUB_STATUS 0x00 /* Status Register offset */ +#define PHUB_CONTROL 0x04 /* Control Register offset */ +#define PHUB_TIMEOUT 0x05 /* Time out value for Status Register */ +#define PCH_PHUB_ROM_WRITE_ENABLE 0x01 /* Enabling for writing ROM */ +#define PCH_PHUB_ROM_WRITE_DISABLE 0x00 /* Disabling for writing ROM */ +#define PCH_PHUB_ROM_START_ADDR 0x14 /* ROM data area start address offset */ + +/* MAX number of INT_REDUCE_CONTROL registers */ +#define MAX_NUM_INT_REDUCE_CONTROL_REG 128 +#define PCI_DEVICE_ID_PCH1_PHUB 0x8801 +#define PCH_MINOR_NOS 1 +#define CLKCFG_CAN_50MHZ 0x12000000 +#define CLKCFG_CANCLK_MASK 0xFF000000 + +/* SROM ACCESS Macro */ +#define PCH_WORD_ADDR_MASK (~((1 << 2) - 1)) + +/* Registers address offset */ +#define PCH_PHUB_ID_REG 0x0000 +#define PCH_PHUB_QUEUE_PRI_VAL_REG 0x0004 +#define PCH_PHUB_RC_QUEUE_MAXSIZE_REG 0x0008 +#define PCH_PHUB_BRI_QUEUE_MAXSIZE_REG 0x000C +#define PCH_PHUB_COMP_RESP_TIMEOUT_REG 0x0010 +#define PCH_PHUB_BUS_SLAVE_CONTROL_REG 0x0014 +#define PCH_PHUB_DEADLOCK_AVOID_TYPE_REG 0x0018 +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG0 0x0020 +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG1 0x0024 +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG2 0x0028 +#define PCH_PHUB_INTPIN_REG_WPERMIT_REG3 0x002C +#define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE 0x0040 +#define CLKCFG_REG_OFFSET 0x500 + +#define PCH_PHUB_OROM_SIZE 15360 + +/** + * struct pch_phub_reg - PHUB register structure + * @phub_id_reg: PHUB_ID register val + * @q_pri_val_reg: QUEUE_PRI_VAL register val + * @rc_q_maxsize_reg: RC_QUEUE_MAXSIZE register val + * @bri_q_maxsize_reg: BRI_QUEUE_MAXSIZE register val + * @comp_resp_timeout_reg: COMP_RESP_TIMEOUT register val + * @bus_slave_control_reg: BUS_SLAVE_CONTROL_REG register val + * @deadlock_avoid_type_reg: DEADLOCK_AVOID_TYPE register val + * @intpin_reg_wpermit_reg0: INTPIN_REG_WPERMIT register 0 val + * @intpin_reg_wpermit_reg1: INTPIN_REG_WPERMIT register 1 val + * @intpin_reg_wpermit_reg2: INTPIN_REG_WPERMIT register 2 val + * @intpin_reg_wpermit_reg3: INTPIN_REG_WPERMIT register 3 val + * @int_reduce_control_reg: INT_REDUCE_CONTROL registers val + * @clkcfg_reg: CLK CFG register val + * @pch_phub_base_address: Register base address + * @pch_phub_extrom_base_address: external rom base address + */ +struct pch_phub_reg { + u32 phub_id_reg; + u32 q_pri_val_reg; + u32 rc_q_maxsize_reg; + u32 bri_q_maxsize_reg; + u32 comp_resp_timeout_reg; + u32 bus_slave_control_reg; + u32 deadlock_avoid_type_reg; + u32 intpin_reg_wpermit_reg0; + u32 intpin_reg_wpermit_reg1; + u32 intpin_reg_wpermit_reg2; + u32 intpin_reg_wpermit_reg3; + u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG]; + u32 clkcfg_reg; + void __iomem *pch_phub_base_address; + void __iomem *pch_phub_extrom_base_address; +}; + +/* SROM SPEC for MAC address assignment offset */ +static const int pch_phub_mac_offset[ETH_ALEN] = {0x3, 0x2, 0x1, 0x0, 0xb, 0xa}; + +static DEFINE_MUTEX(pch_phub_mutex); + +/** + * pch_phub_read_modify_write_reg() - Reading modifying and writing register + * @reg_addr_offset: Register offset address value. + * @data: Writing value. + * @mask: Mask value. + */ +static void pch_phub_read_modify_write_reg(struct pch_phub_reg *chip, + unsigned int reg_addr_offset, + unsigned int data, unsigned int mask) +{ + void __iomem *reg_addr = chip->pch_phub_base_address + reg_addr_offset; + iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr); +} + +/* pch_phub_save_reg_conf - saves register configuration */ +static void pch_phub_save_reg_conf(struct pci_dev *pdev) +{ + unsigned int i; + struct pch_phub_reg *chip = pci_get_drvdata(pdev); + + void __iomem *p = chip->pch_phub_base_address; + + chip->phub_id_reg = ioread32(p + PCH_PHUB_ID_REG); + chip->q_pri_val_reg = ioread32(p + PCH_PHUB_QUEUE_PRI_VAL_REG); + chip->rc_q_maxsize_reg = ioread32(p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG); + chip->bri_q_maxsize_reg = ioread32(p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG); + chip->comp_resp_timeout_reg = + ioread32(p + PCH_PHUB_COMP_RESP_TIMEOUT_REG); + chip->bus_slave_control_reg = + ioread32(p + PCH_PHUB_BUS_SLAVE_CONTROL_REG); + chip->deadlock_avoid_type_reg = + ioread32(p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG); + chip->intpin_reg_wpermit_reg0 = + ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0); + chip->intpin_reg_wpermit_reg1 = + ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1); + chip->intpin_reg_wpermit_reg2 = + ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2); + chip->intpin_reg_wpermit_reg3 = + ioread32(p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3); + dev_dbg(&pdev->dev, "%s : " + "chip->phub_id_reg=%x, " + "chip->q_pri_val_reg=%x, " + "chip->rc_q_maxsize_reg=%x, " + "chip->bri_q_maxsize_reg=%x, " + "chip->comp_resp_timeout_reg=%x, " + "chip->bus_slave_control_reg=%x, " + "chip->deadlock_avoid_type_reg=%x, " + "chip->intpin_reg_wpermit_reg0=%x, " + "chip->intpin_reg_wpermit_reg1=%x, " + "chip->intpin_reg_wpermit_reg2=%x, " + "chip->intpin_reg_wpermit_reg3=%x\n", __func__, + chip->phub_id_reg, + chip->q_pri_val_reg, + chip->rc_q_maxsize_reg, + chip->bri_q_maxsize_reg, + chip->comp_resp_timeout_reg, + chip->bus_slave_control_reg, + chip->deadlock_avoid_type_reg, + chip->intpin_reg_wpermit_reg0, + chip->intpin_reg_wpermit_reg1, + chip->intpin_reg_wpermit_reg2, + chip->intpin_reg_wpermit_reg3); + for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) { + chip->int_reduce_control_reg[i] = + ioread32(p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i); + dev_dbg(&pdev->dev, "%s : " + "chip->int_reduce_control_reg[%d]=%x\n", + __func__, i, chip->int_reduce_control_reg[i]); + } + chip->clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET); +} + +/* pch_phub_restore_reg_conf - restore register configuration */ +static void pch_phub_restore_reg_conf(struct pci_dev *pdev) +{ + unsigned int i; + struct pch_phub_reg *chip = pci_get_drvdata(pdev); + void __iomem *p; + p = chip->pch_phub_base_address; + + iowrite32(chip->phub_id_reg, p + PCH_PHUB_ID_REG); + iowrite32(chip->q_pri_val_reg, p + PCH_PHUB_QUEUE_PRI_VAL_REG); + iowrite32(chip->rc_q_maxsize_reg, p + PCH_PHUB_RC_QUEUE_MAXSIZE_REG); + iowrite32(chip->bri_q_maxsize_reg, p + PCH_PHUB_BRI_QUEUE_MAXSIZE_REG); + iowrite32(chip->comp_resp_timeout_reg, + p + PCH_PHUB_COMP_RESP_TIMEOUT_REG); + iowrite32(chip->bus_slave_control_reg, + p + PCH_PHUB_BUS_SLAVE_CONTROL_REG); + iowrite32(chip->deadlock_avoid_type_reg, + p + PCH_PHUB_DEADLOCK_AVOID_TYPE_REG); + iowrite32(chip->intpin_reg_wpermit_reg0, + p + PCH_PHUB_INTPIN_REG_WPERMIT_REG0); + iowrite32(chip->intpin_reg_wpermit_reg1, + p + PCH_PHUB_INTPIN_REG_WPERMIT_REG1); + iowrite32(chip->intpin_reg_wpermit_reg2, + p + PCH_PHUB_INTPIN_REG_WPERMIT_REG2); + iowrite32(chip->intpin_reg_wpermit_reg3, + p + PCH_PHUB_INTPIN_REG_WPERMIT_REG3); + dev_dbg(&pdev->dev, "%s : " + "chip->phub_id_reg=%x, " + "chip->q_pri_val_reg=%x, " + "chip->rc_q_maxsize_reg=%x, " + "chip->bri_q_maxsize_reg=%x, " + "chip->comp_resp_timeout_reg=%x, " + "chip->bus_slave_control_reg=%x, " + "chip->deadlock_avoid_type_reg=%x, " + "chip->intpin_reg_wpermit_reg0=%x, " + "chip->intpin_reg_wpermit_reg1=%x, " + "chip->intpin_reg_wpermit_reg2=%x, " + "chip->intpin_reg_wpermit_reg3=%x\n", __func__, + chip->phub_id_reg, + chip->q_pri_val_reg, + chip->rc_q_maxsize_reg, + chip->bri_q_maxsize_reg, + chip->comp_resp_timeout_reg, + chip->bus_slave_control_reg, + chip->deadlock_avoid_type_reg, + chip->intpin_reg_wpermit_reg0, + chip->intpin_reg_wpermit_reg1, + chip->intpin_reg_wpermit_reg2, + chip->intpin_reg_wpermit_reg3); + for (i = 0; i < MAX_NUM_INT_REDUCE_CONTROL_REG; i++) { + iowrite32(chip->int_reduce_control_reg[i], + p + PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE + 4 * i); + dev_dbg(&pdev->dev, "%s : " + "chip->int_reduce_control_reg[%d]=%x\n", + __func__, i, chip->int_reduce_control_reg[i]); + } + + iowrite32(chip->clkcfg_reg, p + CLKCFG_REG_OFFSET); +} + +/** + * pch_phub_read_serial_rom() - Reading Serial ROM + * @offset_address: Serial ROM offset address to read. + * @data: Read buffer for specified Serial ROM value. + */ +static void pch_phub_read_serial_rom(struct pch_phub_reg *chip, + unsigned int offset_address, u8 *data) +{ + void __iomem *mem_addr = chip->pch_phub_extrom_base_address + + offset_address; + + *data = ioread8(mem_addr); +} + +/** + * pch_phub_write_serial_rom() - Writing Serial ROM + * @offset_address: Serial ROM offset address. + * @data: Serial ROM value to write. + */ +static int pch_phub_write_serial_rom(struct pch_phub_reg *chip, + unsigned int offset_address, u8 data) +{ + void __iomem *mem_addr = chip->pch_phub_extrom_base_address + + (offset_address & PCH_WORD_ADDR_MASK); + int i; + unsigned int word_data; + unsigned int pos; + unsigned int mask; + pos = (offset_address % 4) * 8; + mask = ~(0xFF << pos); + + iowrite32(PCH_PHUB_ROM_WRITE_ENABLE, + chip->pch_phub_extrom_base_address + PHUB_CONTROL); + + word_data = ioread32(mem_addr); + iowrite32((word_data & mask) | (u32)data << pos, mem_addr); + + i = 0; + while (ioread8(chip->pch_phub_extrom_base_address + + PHUB_STATUS) != 0x00) { + msleep(1); + if (i == PHUB_TIMEOUT) + return -ETIMEDOUT; + i++; + } + + iowrite32(PCH_PHUB_ROM_WRITE_DISABLE, + chip->pch_phub_extrom_base_address + PHUB_CONTROL); + + return 0; +} + +/** + * pch_phub_read_serial_rom_val() - Read Serial ROM value + * @offset_address: Serial ROM address offset value. + * @data: Serial ROM value to read. + */ +static void pch_phub_read_serial_rom_val(struct pch_phub_reg *chip, + unsigned int offset_address, u8 *data) +{ + unsigned int mem_addr; + + mem_addr = PCH_PHUB_ROM_START_ADDR + + pch_phub_mac_offset[offset_address]; + + pch_phub_read_serial_rom(chip, mem_addr, data); +} + +/** + * pch_phub_write_serial_rom_val() - writing Serial ROM value + * @offset_address: Serial ROM address offset value. + * @data: Serial ROM value. + */ +static int pch_phub_write_serial_rom_val(struct pch_phub_reg *chip, + unsigned int offset_address, u8 data) +{ + int retval; + unsigned int mem_addr; + + mem_addr = PCH_PHUB_ROM_START_ADDR + + pch_phub_mac_offset[offset_address]; + + retval = pch_phub_write_serial_rom(chip, mem_addr, data); + + return retval; +} + +/* pch_phub_gbe_serial_rom_conf - makes Serial ROM header format configuration + * for Gigabit Ethernet MAC address + */ +static int pch_phub_gbe_serial_rom_conf(struct pch_phub_reg *chip) +{ + int retval; + + retval = pch_phub_write_serial_rom(chip, 0x0b, 0xbc); + retval |= pch_phub_write_serial_rom(chip, 0x0a, 0x10); + retval |= pch_phub_write_serial_rom(chip, 0x09, 0x01); + retval |= pch_phub_write_serial_rom(chip, 0x08, 0x02); + + retval |= pch_phub_write_serial_rom(chip, 0x0f, 0x00); + retval |= pch_phub_write_serial_rom(chip, 0x0e, 0x00); + retval |= pch_phub_write_serial_rom(chip, 0x0d, 0x00); + retval |= pch_phub_write_serial_rom(chip, 0x0c, 0x80); + + retval |= pch_phub_write_serial_rom(chip, 0x13, 0xbc); + retval |= pch_phub_write_serial_rom(chip, 0x12, 0x10); + retval |= pch_phub_write_serial_rom(chip, 0x11, 0x01); + retval |= pch_phub_write_serial_rom(chip, 0x10, 0x18); + + retval |= pch_phub_write_serial_rom(chip, 0x1b, 0xbc); + retval |= pch_phub_write_serial_rom(chip, 0x1a, 0x10); + retval |= pch_phub_write_serial_rom(chip, 0x19, 0x01); + retval |= pch_phub_write_serial_rom(chip, 0x18, 0x19); + + retval |= pch_phub_write_serial_rom(chip, 0x23, 0xbc); + retval |= pch_phub_write_serial_rom(chip, 0x22, 0x10); + retval |= pch_phub_write_serial_rom(chip, 0x21, 0x01); + retval |= pch_phub_write_serial_rom(chip, 0x20, 0x3a); + + retval |= pch_phub_write_serial_rom(chip, 0x27, 0x01); + retval |= pch_phub_write_serial_rom(chip, 0x26, 0x00); + retval |= pch_phub_write_serial_rom(chip, 0x25, 0x00); + retval |= pch_phub_write_serial_rom(chip, 0x24, 0x00); + + return retval; +} + +/** + * pch_phub_read_gbe_mac_addr() - Read Gigabit Ethernet MAC address + * @offset_address: Gigabit Ethernet MAC address offset value. + * @data: Buffer of the Gigabit Ethernet MAC address value. + */ +static void pch_phub_read_gbe_mac_addr(struct pch_phub_reg *chip, u8 *data) +{ + int i; + for (i = 0; i < ETH_ALEN; i++) + pch_phub_read_serial_rom_val(chip, i, &data[i]); +} + +/** + * pch_phub_write_gbe_mac_addr() - Write MAC address + * @offset_address: Gigabit Ethernet MAC address offset value. + * @data: Gigabit Ethernet MAC address value. + */ +static int pch_phub_write_gbe_mac_addr(struct pch_phub_reg *chip, u8 *data) +{ + int retval; + int i; + + retval = pch_phub_gbe_serial_rom_conf(chip); + if (retval) + return retval; + + for (i = 0; i < ETH_ALEN; i++) { + retval = pch_phub_write_serial_rom_val(chip, i, data[i]); + if (retval) + return retval; + } + + return retval; +} + +static ssize_t pch_phub_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + unsigned int rom_signature; + unsigned char rom_length; + unsigned int tmp; + unsigned int addr_offset; + unsigned int orom_size; + int ret; + int err; + + struct pch_phub_reg *chip = + dev_get_drvdata(container_of(kobj, struct device, kobj)); + + ret = mutex_lock_interruptible(&pch_phub_mutex); + if (ret) { + err = -ERESTARTSYS; + goto return_err_nomutex; + } + + /* Get Rom signature */ + pch_phub_read_serial_rom(chip, 0x80, (unsigned char *)&rom_signature); + rom_signature &= 0xff; + pch_phub_read_serial_rom(chip, 0x81, (unsigned char *)&tmp); + rom_signature |= (tmp & 0xff) << 8; + if (rom_signature == 0xAA55) { + pch_phub_read_serial_rom(chip, 0x82, &rom_length); + orom_size = rom_length * 512; + if (orom_size < off) { + addr_offset = 0; + goto return_ok; + } + if (orom_size < count) { + addr_offset = 0; + goto return_ok; + } + + for (addr_offset = 0; addr_offset < count; addr_offset++) { + pch_phub_read_serial_rom(chip, 0x80 + addr_offset + off, + &buf[addr_offset]); + } + } else { + err = -ENODATA; + goto return_err; + } +return_ok: + mutex_unlock(&pch_phub_mutex); + return addr_offset; + +return_err: + mutex_unlock(&pch_phub_mutex); +return_err_nomutex: + return err; +} + +static ssize_t pch_phub_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + int err; + unsigned int addr_offset; + int ret; + struct pch_phub_reg *chip = + dev_get_drvdata(container_of(kobj, struct device, kobj)); + + ret = mutex_lock_interruptible(&pch_phub_mutex); + if (ret) + return -ERESTARTSYS; + + if (off > PCH_PHUB_OROM_SIZE) { + addr_offset = 0; + goto return_ok; + } + if (count > PCH_PHUB_OROM_SIZE) { + addr_offset = 0; + goto return_ok; + } + + for (addr_offset = 0; addr_offset < count; addr_offset++) { + if (PCH_PHUB_OROM_SIZE < off + addr_offset) + goto return_ok; + + ret = pch_phub_write_serial_rom(chip, 0x80 + addr_offset + off, + buf[addr_offset]); + if (ret) { + err = ret; + goto return_err; + } + } + +return_ok: + mutex_unlock(&pch_phub_mutex); + return addr_offset; + +return_err: + mutex_unlock(&pch_phub_mutex); + return err; +} + +static ssize_t show_pch_mac(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u8 mac[8]; + struct pch_phub_reg *chip = dev_get_drvdata(dev); + + pch_phub_read_gbe_mac_addr(chip, mac); + + return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +} + +static ssize_t store_pch_mac(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u8 mac[6]; + struct pch_phub_reg *chip = dev_get_drvdata(dev); + + if (count != 18) + return -EINVAL; + + sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", + (u32 *)&mac[0], (u32 *)&mac[1], (u32 *)&mac[2], (u32 *)&mac[3], + (u32 *)&mac[4], (u32 *)&mac[5]); + + pch_phub_write_gbe_mac_addr(chip, mac); + + return count; +} + +static DEVICE_ATTR(pch_mac, S_IRUGO | S_IWUSR, show_pch_mac, store_pch_mac); + +static struct bin_attribute pch_bin_attr = { + .attr = { + .name = "pch_firmware", + .mode = S_IRUGO | S_IWUSR, + }, + .size = PCH_PHUB_OROM_SIZE + 1, + .read = pch_phub_bin_read, + .write = pch_phub_bin_write, +}; + +static int __devinit pch_phub_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int retval; + + int ret; + unsigned int rom_size; + struct pch_phub_reg *chip; + + chip = kzalloc(sizeof(struct pch_phub_reg), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s : pci_enable_device FAILED(ret=%d)", __func__, ret); + goto err_pci_enable_dev; + } + dev_dbg(&pdev->dev, "%s : pci_enable_device returns %d\n", __func__, + ret); + + ret = pci_request_regions(pdev, KBUILD_MODNAME); + if (ret) { + dev_err(&pdev->dev, + "%s : pci_request_regions FAILED(ret=%d)", __func__, ret); + goto err_req_regions; + } + dev_dbg(&pdev->dev, "%s : " + "pci_request_regions returns %d\n", __func__, ret); + + chip->pch_phub_base_address = pci_iomap(pdev, 1, 0); + + + if (chip->pch_phub_base_address == 0) { + dev_err(&pdev->dev, "%s : pci_iomap FAILED", __func__); + ret = -ENOMEM; + goto err_pci_iomap; + } + dev_dbg(&pdev->dev, "%s : pci_iomap SUCCESS and value " + "in pch_phub_base_address variable is 0x%08x\n", __func__, + (unsigned int)chip->pch_phub_base_address); + chip->pch_phub_extrom_base_address = pci_map_rom(pdev, &rom_size); + + if (chip->pch_phub_extrom_base_address == 0) { + dev_err(&pdev->dev, "%s : pci_map_rom FAILED", __func__); + ret = -ENOMEM; + goto err_pci_map; + } + dev_dbg(&pdev->dev, "%s : " + "pci_map_rom SUCCESS and value in " + "pch_phub_extrom_base_address variable is 0x%08x\n", __func__, + (unsigned int)chip->pch_phub_extrom_base_address); + + pci_set_drvdata(pdev, chip); + + retval = sysfs_create_file(&pdev->dev.kobj, &dev_attr_pch_mac.attr); + if (retval) + goto err_sysfs_create; + + retval = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr); + if (retval) + goto exit_bin_attr; + + pch_phub_read_modify_write_reg(chip, (unsigned int)CLKCFG_REG_OFFSET, + CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK); + + /* set the prefech value */ + iowrite32(0x000affaa, chip->pch_phub_base_address + 0x14); + /* set the interrupt delay value */ + iowrite32(0x25, chip->pch_phub_base_address + 0x44); + + return 0; +exit_bin_attr: + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_pch_mac.attr); + +err_sysfs_create: + pci_unmap_rom(pdev, chip->pch_phub_extrom_base_address); +err_pci_map: + pci_iounmap(pdev, chip->pch_phub_base_address); +err_pci_iomap: + pci_release_regions(pdev); +err_req_regions: + pci_disable_device(pdev); +err_pci_enable_dev: + kfree(chip); + dev_err(&pdev->dev, "%s returns %d\n", __func__, ret); + return ret; +} + +static void __devexit pch_phub_remove(struct pci_dev *pdev) +{ + struct pch_phub_reg *chip = pci_get_drvdata(pdev); + + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_pch_mac.attr); + sysfs_remove_bin_file(&pdev->dev.kobj, &pch_bin_attr); + pci_unmap_rom(pdev, chip->pch_phub_extrom_base_address); + pci_iounmap(pdev, chip->pch_phub_base_address); + pci_release_regions(pdev); + pci_disable_device(pdev); + kfree(chip); +} + +#ifdef CONFIG_PM + +static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state) +{ + int ret; + + pch_phub_save_reg_conf(pdev); + ret = pci_save_state(pdev); + if (ret) { + dev_err(&pdev->dev, + " %s -pci_save_state returns %d\n", __func__, ret); + return ret; + } + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int pch_phub_resume(struct pci_dev *pdev) +{ + int ret; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s-pci_enable_device failed(ret=%d) ", __func__, ret); + return ret; + } + + pci_enable_wake(pdev, PCI_D3hot, 0); + pch_phub_restore_reg_conf(pdev); + + return 0; +} +#else +#define pch_phub_suspend NULL +#define pch_phub_resume NULL +#endif /* CONFIG_PM */ + +static struct pci_device_id pch_phub_pcidev_id[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH1_PHUB)}, + {0,} +}; + +static struct pci_driver pch_phub_driver = { + .name = "pch_phub", + .id_table = pch_phub_pcidev_id, + .probe = pch_phub_probe, + .remove = __devexit_p(pch_phub_remove), + .suspend = pch_phub_suspend, + .resume = pch_phub_resume +}; + +static int __init pch_phub_pci_init(void) +{ + return pci_register_driver(&pch_phub_driver); +} + +static void __exit pch_phub_pci_exit(void) +{ + pci_unregister_driver(&pch_phub_driver); +} + +module_init(pch_phub_pci_init); +module_exit(pch_phub_pci_exit); + +MODULE_DESCRIPTION("PCH Packet Hub PCI Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From e52eec13cd6b7f30ab19081b387813e03e592ae5 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 8 Sep 2010 16:54:17 +0200 Subject: SYSFS: Allow boot time switching between deprecated and modern sysfs layout I have some systems which need legacy sysfs due to old tools that are making assumptions that a directory can never be a symlink to another directory, and it's a big hazzle to compile separate kernels for them. This patch turns CONFIG_SYSFS_DEPRECATED into a run time option that can be switched on/off the kernel command line. This way the same binary can be used in both cases with just a option on the command line. The old CONFIG_SYSFS_DEPRECATED_V2 option is still there to set the default. I kept the weird name to not break existing config files. Also the compat code can be still completely disabled by undefining CONFIG_SYSFS_DEPRECATED_SWITCH -- just the optimizer takes care of this now instead of lots of ifdefs. This makes the code look nicer. v2: This is an updated version on top of Kay's patch to only handle the block devices. I tested it on my old systems and that seems to work. Cc: axboe@kernel.dk Signed-off-by: Andi Kleen Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- Documentation/kernel-parameters.txt | 9 +++++++++ block/genhd.c | 7 ++----- drivers/base/class.c | 4 ++-- drivers/base/core.c | 26 +++++++++++++++++--------- fs/partitions/check.c | 19 +++++++++---------- include/linux/device.h | 7 +++++++ init/Kconfig | 26 ++++++++++++++++++++++---- 7 files changed, 68 insertions(+), 30 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 3d854c9ae53a..67fa3fdc128f 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2365,6 +2365,15 @@ and is between 256 and 4096 characters. It is defined in the file switches= [HW,M68k] + sysfs.deprecated=0|1 [KNL] + Enable/disable old style sysfs layout for old udev + on older distributions. When this option is enabled + very new udev will not work anymore. When this option + is disabled (or CONFIG_SYSFS_DEPRECATED not compiled) + in older udev will not work anymore. + Default depends on CONFIG_SYSFS_DEPRECATED_V2 set in + the kernel configuration. + sysrq_always_enabled [KNL] Ignore sysrq setting - this boot parameter will diff --git a/block/genhd.c b/block/genhd.c index 59a2db6fecef..4e28a840bde0 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -22,9 +22,7 @@ #include "blk.h" static DEFINE_MUTEX(block_class_lock); -#ifndef CONFIG_SYSFS_DEPRECATED struct kobject *block_depr; -#endif /* for extended dynamic devt allocation, currently only one major is used */ #define MAX_EXT_DEVT (1 << MINORBITS) @@ -803,10 +801,9 @@ static int __init genhd_device_init(void) register_blkdev(BLOCK_EXT_MAJOR, "blkext"); -#ifndef CONFIG_SYSFS_DEPRECATED /* create top-level block dir */ - block_depr = kobject_create_and_add("block", NULL); -#endif + if (!sysfs_deprecated) + block_depr = kobject_create_and_add("block", NULL); return 0; } diff --git a/drivers/base/class.c b/drivers/base/class.c index 1078969889fd..9c63a5687d69 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -184,9 +184,9 @@ int __class_register(struct class *cls, struct lock_class_key *key) if (!cls->dev_kobj) cls->dev_kobj = sysfs_dev_char_kobj; -#if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK) +#if defined(CONFIG_BLOCK) /* let the block class directory show up in the root of sysfs */ - if (cls != &block_class) + if (!sysfs_deprecated || cls != &block_class) cp->class_subsys.kobj.kset = class_kset; #else cp->class_subsys.kobj.kset = class_kset; diff --git a/drivers/base/core.c b/drivers/base/core.c index 6cf9069f3150..f7f906f0a2f2 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -26,6 +26,19 @@ #include "base.h" #include "power/power.h" +#ifdef CONFIG_SYSFS_DEPRECATED +#ifdef CONFIG_SYSFS_DEPRECATED_V2 +long sysfs_deprecated = 1; +#else +long sysfs_deprecated = 0; +#endif +static __init int sysfs_deprecated_setup(char *arg) +{ + return strict_strtol(arg, 10, &sysfs_deprecated); +} +early_param("sysfs.deprecated", sysfs_deprecated_setup); +#endif + int (*platform_notify)(struct device *dev) = NULL; int (*platform_notify_remove)(struct device *dev) = NULL; static struct kobject *dev_kobj; @@ -617,14 +630,13 @@ static struct kobject *get_device_parent(struct device *dev, struct kobject *parent_kobj; struct kobject *k; -#ifdef CONFIG_SYSFS_DEPRECATED /* block disks show up in /sys/block */ - if (dev->class == &block_class) { + if (sysfs_deprecated && dev->class == &block_class) { if (parent && parent->class == &block_class) return &parent->kobj; return &block_class.p->class_subsys.kobj; } -#endif + /* * If we have no parent, we live in "virtual". * Class-devices with a non class-device as parent, live @@ -707,11 +719,9 @@ static int device_add_class_symlinks(struct device *dev) goto out_subsys; } -#ifdef CONFIG_SYSFS_DEPRECATED /* /sys/block has directories and does not need symlinks */ - if (dev->class == &block_class) + if (sysfs_deprecated && dev->class == &block_class) return 0; -#endif /* link in the class directory pointing to the device */ error = sysfs_create_link(&dev->class->p->class_subsys.kobj, @@ -738,10 +748,8 @@ static void device_remove_class_symlinks(struct device *dev) if (dev->parent && device_is_not_partition(dev)) sysfs_remove_link(&dev->kobj, "device"); sysfs_remove_link(&dev->kobj, "subsystem"); -#ifdef CONFIG_SYSFS_DEPRECATED - if (dev->class == &block_class) + if (sysfs_deprecated && dev->class == &block_class) return; -#endif sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev)); } diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 79fbf3f390f0..137bf9787853 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -513,14 +513,14 @@ void register_disk(struct gendisk *disk) if (device_add(ddev)) return; -#ifndef CONFIG_SYSFS_DEPRECATED - err = sysfs_create_link(block_depr, &ddev->kobj, - kobject_name(&ddev->kobj)); - if (err) { - device_del(ddev); - return; + if (!sysfs_deprecated) { + err = sysfs_create_link(block_depr, &ddev->kobj, + kobject_name(&ddev->kobj)); + if (err) { + device_del(ddev); + return; + } } -#endif disk->part0.holder_dir = kobject_create_and_add("holders", &ddev->kobj); disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj); @@ -737,8 +737,7 @@ void del_gendisk(struct gendisk *disk) kobject_put(disk->part0.holder_dir); kobject_put(disk->slave_dir); disk->driverfs_dev = NULL; -#ifndef CONFIG_SYSFS_DEPRECATED - sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); -#endif + if (!sysfs_deprecated) + sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); device_del(disk_to_dev(disk)); } diff --git a/include/linux/device.h b/include/linux/device.h index 516fecacf27b..dd4895313468 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -751,4 +751,11 @@ do { \ MODULE_ALIAS("char-major-" __stringify(major) "-" __stringify(minor)) #define MODULE_ALIAS_CHARDEV_MAJOR(major) \ MODULE_ALIAS("char-major-" __stringify(major) "-*") + +#ifdef CONFIG_SYSFS_DEPRECATED +extern long sysfs_deprecated; +#else +#define sysfs_deprecated 0 +#endif + #endif /* _DEVICE_H_ */ diff --git a/init/Kconfig b/init/Kconfig index 137609f33ebc..d742b6fca8d2 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -660,8 +660,12 @@ config SYSFS_DEPRECATED depends on SYSFS default n help - This option switches the layout of the "block" class devices, to not - show up in /sys/class/block/, but only in /sys/block/. + This option adds code that switches the layout of the "block" class + devices, to not show up in /sys/class/block/, but only in + /sys/block/. + + This switch is only active when the sysfs.deprecated=1 boot option is + passed or the SYSFS_DEPRECATED_V2 option is set. This option allows new kernels to run on old distributions and tools, which might get confused by /sys/class/block/. Since 2007/2008 all @@ -672,8 +676,22 @@ config SYSFS_DEPRECATED option enabled. Only if you are using a new kernel on an old distribution, you might - need to say Y here. Never say Y, if the original kernel, that came - with your distribution, has not set this option. + need to say Y here. + +config SYSFS_DEPRECATED_V2 + bool "enabled deprecated sysfs features by default" + default n + depends on SYSFS + depends on SYSFS_DEPRECATED + help + Enable deprecated sysfs by default. + + See the CONFIG_SYSFS_DEPRECATED option for more details about this + option. + + Only if you are using a new kernel on an old distribution, you might + need to say Y here. Even then, odds are you would not need it + enabled, you can always pass the boot option if absolutely necessary. config RELAY bool "Kernel->user space relay support (formerly relayfs)" -- cgit v1.2.3-70-g09d2 From 24b4b67d17c308aaa956b73ab1e88190f6642bbe Mon Sep 17 00:00:00 2001 From: Samo Pogacnik Date: Wed, 25 Aug 2010 20:44:07 +0200 Subject: add ttyprintk driver Ttyprintk is a pseudo TTY driver, which allows users to make printk messages, via output to ttyprintk device. It is possible to store "console" messages inline with kernel messages for better analyses of the boot process, for example. Signed-off-by: Samo Pogacnik Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- Documentation/devices.txt | 1 + drivers/char/Kconfig | 15 ++++ drivers/char/Makefile | 1 + drivers/char/ttyprintk.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 242 insertions(+) create mode 100644 drivers/char/ttyprintk.c (limited to 'Documentation') diff --git a/Documentation/devices.txt b/Documentation/devices.txt index d0d1df6cb5de..6a08fd731d28 100644 --- a/Documentation/devices.txt +++ b/Documentation/devices.txt @@ -239,6 +239,7 @@ Your cooperation is appreciated. 0 = /dev/tty Current TTY device 1 = /dev/console System console 2 = /dev/ptmx PTY master multiplex + 3 = /dev/ttyprintk User messages via printk TTY device 64 = /dev/cua0 Callout device for ttyS0 ... 255 = /dev/cua191 Callout device for ttyS191 diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 3d44ec724c17..43d3395325c5 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -493,6 +493,21 @@ config LEGACY_PTY_COUNT When not in use, each legacy PTY occupies 12 bytes on 32-bit architectures and 24 bytes on 64-bit architectures. +config TTY_PRINTK + bool "TTY driver to output user messages via printk" + depends on EMBEDDED + default n + ---help--- + If you say Y here, the support for writing user messages (i.e. + console messages) via printk is available. + + The feature is useful to inline user messages with kernel + messages. + In order to use this feature, you should output user messages + to /dev/ttyprintk or redirect console to this TTY. + + If unsure, say N. + config BRIQ_PANEL tristate 'Total Impact briQ front panel driver' depends on PPC_CHRP diff --git a/drivers/char/Makefile b/drivers/char/Makefile index dc9641660605..3a9c01416839 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -12,6 +12,7 @@ obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o t obj-y += tty_mutex.o obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o +obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o obj-y += misc.o obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c new file mode 100644 index 000000000000..c40c1612c8a7 --- /dev/null +++ b/drivers/char/ttyprintk.c @@ -0,0 +1,225 @@ +/* + * linux/drivers/char/ttyprintk.c + * + * Copyright (C) 2010 Samo Pogacnik + * + * This program is free software; you can redistribute it and/or modify + * it under the smems of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +/* + * This pseudo device allows user to make printk messages. It is possible + * to store "console" messages inline with kernel messages for better analyses + * of the boot process, for example. + */ + +#include +#include +#include + +struct ttyprintk_port { + struct tty_port port; + struct mutex port_write_mutex; +}; + +static struct ttyprintk_port tpk_port; + +/* + * Our simple preformatting supports transparent output of (time-stamped) + * printk messages (also suitable for logging service): + * - any cr is replaced by nl + * - adds a ttyprintk source tag in front of each line + * - too long message is fragmeted, with '\'nl between fragments + * - TPK_STR_SIZE isn't really the write_room limiting factor, bcause + * it is emptied on the fly during preformatting. + */ +#define TPK_STR_SIZE 508 /* should be bigger then max expected line length */ +#define TPK_MAX_ROOM 4096 /* we could assume 4K for instance */ +static const char *tpk_tag = "[U] "; /* U for User */ +static int tpk_curr; + +static int tpk_printk(const unsigned char *buf, int count) +{ + static char tmp[TPK_STR_SIZE + 4]; + int i = tpk_curr; + + if (buf == NULL) { + /* flush tmp[] */ + if (tpk_curr > 0) { + /* non nl or cr terminated message - add nl */ + tmp[tpk_curr + 0] = '\n'; + tmp[tpk_curr + 1] = '\0'; + printk(KERN_INFO "%s%s", tpk_tag, tmp); + tpk_curr = 0; + } + return i; + } + + for (i = 0; i < count; i++) { + tmp[tpk_curr] = buf[i]; + if (tpk_curr < TPK_STR_SIZE) { + switch (buf[i]) { + case '\r': + /* replace cr with nl */ + tmp[tpk_curr + 0] = '\n'; + tmp[tpk_curr + 1] = '\0'; + printk(KERN_INFO "%s%s", tpk_tag, tmp); + tpk_curr = 0; + if (buf[i + 1] == '\n') + i++; + break; + case '\n': + tmp[tpk_curr + 1] = '\0'; + printk(KERN_INFO "%s%s", tpk_tag, tmp); + tpk_curr = 0; + break; + default: + tpk_curr++; + } + } else { + /* end of tmp buffer reached: cut the message in two */ + tmp[tpk_curr + 1] = '\\'; + tmp[tpk_curr + 2] = '\n'; + tmp[tpk_curr + 3] = '\0'; + printk(KERN_INFO "%s%s", tpk_tag, tmp); + tpk_curr = 0; + } + } + + return count; +} + +/* + * TTY operations open function. + */ +static int tpk_open(struct tty_struct *tty, struct file *filp) +{ + tty->driver_data = &tpk_port; + + return tty_port_open(&tpk_port.port, tty, filp); +} + +/* + * TTY operations close function. + */ +static void tpk_close(struct tty_struct *tty, struct file *filp) +{ + struct ttyprintk_port *tpkp = tty->driver_data; + + mutex_lock(&tpkp->port_write_mutex); + /* flush tpk_printk buffer */ + tpk_printk(NULL, 0); + mutex_unlock(&tpkp->port_write_mutex); + + tty_port_close(&tpkp->port, tty, filp); +} + +/* + * TTY operations write function. + */ +static int tpk_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct ttyprintk_port *tpkp = tty->driver_data; + int ret; + + + /* exclusive use of tpk_printk within this tty */ + mutex_lock(&tpkp->port_write_mutex); + ret = tpk_printk(buf, count); + mutex_unlock(&tpkp->port_write_mutex); + + return ret; +} + +/* + * TTY operations write_room function. + */ +static int tpk_write_room(struct tty_struct *tty) +{ + return TPK_MAX_ROOM; +} + +/* + * TTY operations ioctl function. + */ +static int tpk_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ttyprintk_port *tpkp = tty->driver_data; + + if (!tpkp) + return -EINVAL; + + switch (cmd) { + /* Stop TIOCCONS */ + case TIOCCONS: + return -EOPNOTSUPP; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static const struct tty_operations ttyprintk_ops = { + .open = tpk_open, + .close = tpk_close, + .write = tpk_write, + .write_room = tpk_write_room, + .ioctl = tpk_ioctl, +}; + +struct tty_port_operations null_ops = { }; + +static struct tty_driver *ttyprintk_driver; + +static int __init ttyprintk_init(void) +{ + int ret = -ENOMEM; + void *rp; + + ttyprintk_driver = alloc_tty_driver(1); + if (!ttyprintk_driver) + return ret; + + ttyprintk_driver->owner = THIS_MODULE; + ttyprintk_driver->driver_name = "ttyprintk"; + ttyprintk_driver->name = "ttyprintk"; + ttyprintk_driver->major = TTYAUX_MAJOR; + ttyprintk_driver->minor_start = 3; + ttyprintk_driver->num = 1; + ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE; + ttyprintk_driver->init_termios = tty_std_termios; + ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET; + ttyprintk_driver->flags = TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(ttyprintk_driver, &ttyprintk_ops); + + ret = tty_register_driver(ttyprintk_driver); + if (ret < 0) { + printk(KERN_ERR "Couldn't register ttyprintk driver\n"); + goto error; + } + + /* create our unnumbered device */ + rp = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 3), NULL, + ttyprintk_driver->name); + if (IS_ERR(rp)) { + printk(KERN_ERR "Couldn't create ttyprintk device\n"); + ret = PTR_ERR(rp); + goto error; + } + + tty_port_init(&tpk_port.port); + tpk_port.port.ops = &null_ops; + mutex_init(&tpk_port.port_write_mutex); + + return 0; + +error: + put_tty_driver(ttyprintk_driver); + ttyprintk_driver = NULL; + return ret; +} +module_init(ttyprintk_init); -- cgit v1.2.3-70-g09d2 From f4a3e0bceb57466c31757f25e4e0ed108d1299ec Mon Sep 17 00:00:00 2001 From: "Dr. Werner Fink" Date: Wed, 22 Sep 2010 12:45:40 +0200 Subject: tty: Add a new file /proc/tty/consoles Add a new file /proc/tty/consoles to be able to determine the registered system console lines. If the reading process holds /dev/console open at the regular standard input stream the active device will be marked by an asterisk. Show possible operations and also decode the used flags of the listed console lines. Signed-off-by: Werner Fink Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/proc.txt | 32 ++++++++ fs/proc/proc_tty.c | 158 +++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) (limited to 'Documentation') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index a6aca8740883..98223a676940 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -1075,6 +1075,7 @@ Table 1-11: Files in /proc/tty drivers list of drivers and their usage ldiscs registered line disciplines driver/serial usage statistic and status of single tty lines + consoles registered system console lines .............................................................................. To see which tty's are currently in use, you can simply look into the file @@ -1093,6 +1094,37 @@ To see which tty's are currently in use, you can simply look into the file /dev/tty /dev/tty 5 0 system:/dev/tty unknown /dev/tty 4 1-63 console +To see which character device lines are currently used for the system console +/dev/console, you may simply look into the file /proc/tty/consoles: + + > cat /proc/tty/consoles + tty0 -WU (ECp) 4:7 + ttyS0 -W- (Ep) 4:64 + +The columns are: + + device name of the device + operations R = can do read operations + W = can do write operations + U = can do unblank + flags E = it is enabled + C = it is prefered console + B = it is primary boot console + p = it is used for printk buffer + b = it is not a TTY but a Braille device + a = it is safe to use when cpu is offline + * = it is standard input of the reading process + major:minor major and minor number of the device separated by a colon + +If the reading process holds /dev/console open at the regular standard input +stream the active device will be marked by an asterisk: + + > cat /proc/tty/consoles < /dev/console + tty0 -WU (ECp*) 4:7 + ttyS0 -W- (Ep) 4:64 + > tty + /dev/pts/3 + 1.8 Miscellaneous kernel statistics in /proc/stat ------------------------------------------------- diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c index 83adcc869437..dc44f94022f1 100644 --- a/fs/proc/proc_tty.c +++ b/fs/proc/proc_tty.c @@ -12,7 +12,10 @@ #include #include #include +#include +#include #include +#include #include /* @@ -136,6 +139,160 @@ static const struct file_operations proc_tty_drivers_operations = { .release = seq_release, }; +/* + * The device ID of file descriptor 0 of the current reading + * task if a character device... + */ +static dev_t current_dev; + +/* + * This is the handler for /proc/tty/consoles + */ +static int show_console_dev(struct seq_file *m, void *v) +{ + const struct tty_driver *driver; + struct console *con; + int index, len; + char flags[10]; + dev_t dev; + + if (v == SEQ_START_TOKEN) + return 0; + con = (struct console *)v; + if (!con) + return 0; + driver = con->device(con, &index); + if (!driver) + return 0; + dev = MKDEV(driver->major, driver->minor_start) + index; + + index = 0; + if (con->flags & CON_ENABLED) + flags[index++] = 'E'; + if (con->flags & CON_CONSDEV) + flags[index++] = 'C'; + if (con->flags & CON_BOOT) + flags[index++] = 'B'; + if (con->flags & CON_PRINTBUFFER) + flags[index++] = 'p'; + if (con->flags & CON_BRL) + flags[index++] = 'b'; + if (con->flags & CON_ANYTIME) + flags[index++] = 'a'; + if (current_dev == dev) + flags[index++] = '*'; + flags[index] = 0; + + seq_printf(m, "%s%d%n", con->name, con->index, &len); + len = 21 - len; + if (len < 1) + len = 1; + seq_printf(m, "%*c", len, ' '); + seq_printf(m, "%c%c%c (%s)%n", con->read ? 'R' : '-', + con->write ? 'W' : '-', con->unblank ? 'U' : '-', + flags, &len); + len = 13 - len; + if (len < 1) + len = 1; + seq_printf(m, "%*c%4d:%d\n", len, ' ', MAJOR(dev), MINOR(dev)); + + return 0; +} + +/* iterator for consoles */ +static void *c_start(struct seq_file *m, loff_t *pos) +{ + struct console *con; + loff_t off = 0; + + if (*pos == 0) + return SEQ_START_TOKEN; + + acquire_console_sem(); + for (con = console_drivers; con; con = con->next) { + if (!con->device) + continue; + if (++off == *pos) + break; + } + release_console_sem(); + + return con; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct console *con; + + acquire_console_sem(); + if (v == SEQ_START_TOKEN) + con = console_drivers; + else + con = ((struct console *)v)->next; + for (; con; con = con->next) { + if (!con->device) + continue; + ++*pos; + break; + } + release_console_sem(); + + return con; +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +static const struct seq_operations tty_consoles_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_console_dev +}; + +/* + * Used for open /proc/tty/consoles. Before this detect + * the device ID of file descriptor 0 of the current + * reading task if a character device... + */ +static int tty_consoles_open(struct inode *inode, struct file *file) +{ + struct files_struct *curfiles; + + current_dev = 0; + curfiles = get_files_struct(current); + if (curfiles) { + const struct file *curfp; + spin_lock(&curfiles->file_lock); + curfp = fcheck_files(curfiles, 0); + if (curfp && curfp->private_data) { + const struct inode *inode; + dget(curfp->f_dentry); + inode = curfp->f_dentry->d_inode; + if (S_ISCHR(inode->i_mode)) { + struct tty_struct *tty; + tty = (struct tty_struct *)curfp->private_data; + if (tty && tty->magic == TTY_MAGIC) { + tty = tty_pair_get_tty(tty); + current_dev = tty_devnum(tty); + } + } + dput(curfp->f_dentry); + } + spin_unlock(&curfiles->file_lock); + put_files_struct(curfiles); + } + return seq_open(file, &tty_consoles_op); +} + +static const struct file_operations proc_tty_consoles_operations = { + .open = tty_consoles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + /* * This function is called by tty_register_driver() to handle * registering the driver's /proc handler into /proc/tty/driver/ @@ -186,4 +343,5 @@ void __init proc_tty_init(void) proc_tty_driver = proc_mkdir_mode("tty/driver", S_IRUSR|S_IXUSR, NULL); proc_create("tty/ldiscs", 0, NULL, &tty_ldiscs_proc_fops); proc_create("tty/drivers", 0, NULL, &proc_tty_drivers_operations); + proc_create("tty/consoles", 0, NULL, &proc_tty_consoles_operations); } -- cgit v1.2.3-70-g09d2 From 834e2312e7a384877a876b0d34dffc3046c96bcb Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 13 Sep 2010 10:43:25 -0400 Subject: USB: teach "devices" file about Wireless and SuperSpeed USB The /sys/kernel/debug/usb/devices file doesn't know about Wireless or SuperSpeed USB. This patch (as1416b) teaches it, and updates the Documentation/usb/proc_sub_info.txt file accordingly. Signed-off-by: Alan Stern CC: David Vrabel CC: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/proc_usb_info.txt | 34 ++++++++++++++++++++++------------ drivers/usb/core/devices.c | 11 +++++++---- 2 files changed, 29 insertions(+), 16 deletions(-) (limited to 'Documentation') diff --git a/Documentation/usb/proc_usb_info.txt b/Documentation/usb/proc_usb_info.txt index fafcd4723260..afe596d5f201 100644 --- a/Documentation/usb/proc_usb_info.txt +++ b/Documentation/usb/proc_usb_info.txt @@ -1,12 +1,17 @@ /proc/bus/usb filesystem output =============================== -(version 2003.05.30) +(version 2010.09.13) The usbfs filesystem for USB devices is traditionally mounted at /proc/bus/usb. It provides the /proc/bus/usb/devices file, as well as the /proc/bus/usb/BBB/DDD files. +In many modern systems the usbfs filsystem isn't used at all. Instead +USB device nodes are created under /dev/usb/ or someplace similar. The +"devices" file is available in debugfs, typically as +/sys/kernel/debug/usb/devices. + **NOTE**: If /proc/bus/usb appears empty, and a host controller driver has been linked, then you need to mount the @@ -106,8 +111,8 @@ Legend: Topology info: -T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd -| | | | | | | | |__MaxChildren +T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=dddd MxCh=dd +| | | | | | | | |__MaxChildren | | | | | | | |__Device Speed in Mbps | | | | | | |__DeviceNumber | | | | | |__Count of devices at this level @@ -120,8 +125,13 @@ T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd Speed may be: 1.5 Mbit/s for low speed USB 12 Mbit/s for full speed USB - 480 Mbit/s for high speed USB (added for USB 2.0) + 480 Mbit/s for high speed USB (added for USB 2.0); + also used for Wireless USB, which has no fixed speed + 5000 Mbit/s for SuperSpeed USB (added for USB 3.0) + For reasons lost in the mists of time, the Port number is always + too low by 1. For example, a device plugged into port 4 will + show up with "Port=03". Bandwidth info: B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd @@ -291,7 +301,7 @@ Here's an example, from a system which has a UHCI root hub, an external hub connected to the root hub, and a mouse and a serial converter connected to the external hub. -T: Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 +T: Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 B: Alloc= 28/900 us ( 3%), #Int= 2, #Iso= 0 D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=0000 ProdID=0000 Rev= 0.00 @@ -301,21 +311,21 @@ C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=255ms -T: Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 4 +T: Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 4 D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=0451 ProdID=1446 Rev= 1.00 C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=100mA I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub E: Ad=81(I) Atr=03(Int.) MxPS= 1 Ivl=255ms -T: Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=1.5 MxCh= 0 +T: Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=1.5 MxCh= 0 D: Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=04b4 ProdID=0001 Rev= 0.00 C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA I: If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=02 Driver=mouse E: Ad=81(I) Atr=03(Int.) MxPS= 3 Ivl= 10ms -T: Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#= 4 Spd=12 MxCh= 0 +T: Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#= 4 Spd=12 MxCh= 0 D: Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=0565 ProdID=0001 Rev= 1.08 S: Manufacturer=Peracom Networks, Inc. @@ -330,12 +340,12 @@ E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl= 8ms Selecting only the "T:" and "I:" lines from this (for example, by using "procusb ti"), we have: -T: Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 -T: Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 4 +T: Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 +T: Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 4 I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub -T: Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=1.5 MxCh= 0 +T: Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=1.5 MxCh= 0 I: If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=02 Driver=mouse -T: Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#= 4 Spd=12 MxCh= 0 +T: Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#= 4 Spd=12 MxCh= 0 I: If#= 0 Alt= 0 #EPs= 3 Cls=00(>ifc ) Sub=00 Prot=00 Driver=serial diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 3449742c00e1..ddb4dc980923 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -66,8 +66,8 @@ #define ALLOW_SERIAL_NUMBER static const char *format_topo = -/* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd */ -"\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s MxCh=%2d\n"; +/* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=dddd MxCh=dd */ +"\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%-4s MxCh=%2d\n"; static const char *format_string_manufacturer = /* S: Manufacturer=xxxx */ @@ -520,11 +520,14 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, speed = "1.5"; break; case USB_SPEED_UNKNOWN: /* usb 1.1 root hub code */ case USB_SPEED_FULL: - speed = "12 "; break; + speed = "12"; break; + case USB_SPEED_WIRELESS: /* Wireless has no real fixed speed */ case USB_SPEED_HIGH: speed = "480"; break; + case USB_SPEED_SUPER: + speed = "5000"; break; default: - speed = "?? "; + speed = "??"; } data_end = pages_start + sprintf(pages_start, format_topo, bus->busnum, level, parent_devnum, -- cgit v1.2.3-70-g09d2 From 1b62d2583963d5974974aa923214bada7aa7b5d1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Sep 2010 05:01:22 -0700 Subject: USB: yurex: assign a real minor number to the driver This assigns the minor number 192 to the yurex driver. We also fix up the previous usb minor number entry, it was wrong. Cc: Tomoki Sekiyama Signed-off-by: Greg Kroah-Hartman --- Documentation/devices.txt | 5 ++++- drivers/usb/misc/yurex.c | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devices.txt b/Documentation/devices.txt index d0d1df6cb5de..3cab6b342017 100644 --- a/Documentation/devices.txt +++ b/Documentation/devices.txt @@ -2553,7 +2553,10 @@ Your cooperation is appreciated. 175 = /dev/usb/legousbtower15 16th USB Legotower device 176 = /dev/usb/usbtmc1 First USB TMC device ... - 192 = /dev/usb/usbtmc16 16th USB TMC device + 191 = /dev/usb/usbtmc16 16th USB TMC device + 192 = /dev/usb/yurex1 First USB Yurex device + ... + 209 = /dev/usb/yurex16 16th USB Yurex device 240 = /dev/usb/dabusb0 First daubusb device ... 243 = /dev/usb/dabusb3 Fourth dabusb device diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index 76b8ab47fe04..5f6443bc6142 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -47,9 +47,9 @@ static struct usb_device_id yurex_table[] = { MODULE_DEVICE_TABLE(usb, yurex_table); #ifdef CONFIG_USB_DYNAMIC_MINORS -#define YUREX_MINOR_BASE 0 +#define YUREX_MINOR_BASE 0 #else -#define YUREX_MINOR_BASE 224 /* not official yet */ +#define YUREX_MINOR_BASE 192 #endif /* Structure to hold all of our device specific stuff */ -- cgit v1.2.3-70-g09d2 From 230f7ede6c2f0e403f29e03e0251a470aa9350dd Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Tue, 28 Sep 2010 20:55:21 +0200 Subject: USB: add USB EHCI support for MPC5121 SoC Extends FSL EHCI platform driver glue layer to support MPC5121 USB controllers. MPC5121 Rev 2.0 silicon EHCI registers are in big endian format. The appropriate flags are set using the information in the platform data structure. MPC83xx system interface registers are not available on MPC512x, so the access to these registers is isolated in MPC512x case. Furthermore the USB controller clocks must be enabled before 512x register accesses which is done by providing platform specific init callback. The MPC512x internal USB PHY doesn't provide supply voltage. For boards using different power switches allow specifying DRVVBUS and PWR_FAULT signal polarity of the MPC5121 internal PHY using "fsl,invert-drvvbus" and "fsl,invert-pwr-fault" properties in the device tree USB nodes. Adds documentation for this new device tree bindings. Signed-off-by: Anatolij Gustschin Cc: Grant Likely Signed-off-by: Greg Kroah-Hartman --- Documentation/powerpc/dts-bindings/fsl/usb.txt | 22 ++++++ drivers/usb/Kconfig | 1 + drivers/usb/host/Kconfig | 6 +- drivers/usb/host/ehci-fsl.c | 99 ++++++++++++++++++-------- drivers/usb/host/ehci-fsl.h | 13 +++- drivers/usb/host/ehci-mem.c | 2 +- drivers/usb/host/fsl-mph-dr-of.c | 89 +++++++++++++++++++++++ include/linux/fsl_devices.h | 15 ++++ 8 files changed, 215 insertions(+), 32 deletions(-) (limited to 'Documentation') diff --git a/Documentation/powerpc/dts-bindings/fsl/usb.txt b/Documentation/powerpc/dts-bindings/fsl/usb.txt index b00152402694..bd5723f0b67e 100644 --- a/Documentation/powerpc/dts-bindings/fsl/usb.txt +++ b/Documentation/powerpc/dts-bindings/fsl/usb.txt @@ -8,6 +8,7 @@ and additions : Required properties : - compatible : Should be "fsl-usb2-mph" for multi port host USB controllers, or "fsl-usb2-dr" for dual role USB controllers + or "fsl,mpc5121-usb2-dr" for dual role USB controllers of MPC5121 - phy_type : For multi port host USB controllers, should be one of "ulpi", or "serial". For dual role USB controllers, should be one of "ulpi", "utmi", "utmi_wide", or "serial". @@ -33,6 +34,12 @@ Recommended properties : - interrupt-parent : the phandle for the interrupt controller that services interrupts for this device. +Optional properties : + - fsl,invert-drvvbus : boolean; for MPC5121 USB0 only. Indicates the + port power polarity of internal PHY signal DRVVBUS is inverted. + - fsl,invert-pwr-fault : boolean; for MPC5121 USB0 only. Indicates + the PWR_FAULT signal polarity is inverted. + Example multi port host USB controller device node : usb@22000 { compatible = "fsl-usb2-mph"; @@ -57,3 +64,18 @@ Example dual role USB controller device node : dr_mode = "otg"; phy = "ulpi"; }; + +Example dual role USB controller device node for MPC5121ADS: + + usb@4000 { + compatible = "fsl,mpc5121-usb2-dr"; + reg = <0x4000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = < &ipic >; + interrupts = <44 0x8>; + dr_mode = "otg"; + phy_type = "utmi_wide"; + fsl,invert-drvvbus; + fsl,invert-pwr-fault; + }; diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 4aa00e6e57ad..67eb3770868f 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -59,6 +59,7 @@ config USB_ARCH_HAS_OHCI config USB_ARCH_HAS_EHCI boolean default y if PPC_83xx + default y if PPC_MPC512x default y if SOC_AU1200 default y if ARCH_IXP4XX default y if ARCH_W90X900 diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index f3a90b0fc422..bf2e7d234533 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -93,12 +93,14 @@ config USB_EHCI_TT_NEWSCHED config USB_EHCI_BIG_ENDIAN_MMIO bool - depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX) + depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || \ + XPS_USB_HCD_XILINX || PPC_MPC512x) default y config USB_EHCI_BIG_ENDIAN_DESC bool - depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX) + depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ + PPC_MPC512x) default y config XPS_USB_HCD_XILINX diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 8600317bd60b..86e42892016d 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -116,13 +116,33 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, goto err3; } - /* Enable USB controller */ - temp = in_be32(hcd->regs + 0x500); - out_be32(hcd->regs + 0x500, temp | 0x4); + pdata->regs = hcd->regs; - /* Set to Host mode */ - temp = in_le32(hcd->regs + 0x1a8); - out_le32(hcd->regs + 0x1a8, temp | 0x3); + /* + * do platform specific init: check the clock, grab/config pins, etc. + */ + if (pdata->init && pdata->init(pdev)) { + retval = -ENODEV; + goto err3; + } + + /* + * Check if it is MPC5121 SoC, otherwise set pdata->have_sysif_regs + * flag for 83xx or 8536 system interface registers. + */ + if (pdata->big_endian_mmio) + temp = in_be32(hcd->regs + FSL_SOC_USB_ID); + else + temp = in_le32(hcd->regs + FSL_SOC_USB_ID); + + if ((temp & ID_MSK) != (~((temp & NID_MSK) >> 8) & ID_MSK)) + pdata->have_sysif_regs = 1; + + /* Enable USB controller, 83xx or 8536 */ + if (pdata->have_sysif_regs) + setbits32(hcd->regs + FSL_SOC_USB_CTRL, 0x4); + + /* Don't need to set host mode here. It will be done by tdi_reset() */ retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); if (retval != 0) @@ -137,6 +157,8 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, usb_put_hcd(hcd); err1: dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval); + if (pdata->exit) + pdata->exit(pdev); return retval; } @@ -154,17 +176,30 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, static void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct platform_device *pdev) { + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + usb_remove_hcd(hcd); + + /* + * do platform specific un-initialization: + * release iomux pins, disable clock, etc. + */ + if (pdata->exit) + pdata->exit(pdev); iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); } -static void mpc83xx_setup_phy(struct ehci_hcd *ehci, - enum fsl_usb2_phy_modes phy_mode, - unsigned int port_offset) +static void ehci_fsl_setup_phy(struct ehci_hcd *ehci, + enum fsl_usb2_phy_modes phy_mode, + unsigned int port_offset) { - u32 portsc = 0; + u32 portsc; + + portsc = ehci_readl(ehci, &ehci->regs->port_status[port_offset]); + portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW); + switch (phy_mode) { case FSL_USB2_PHY_ULPI: portsc |= PORT_PTS_ULPI; @@ -184,20 +219,21 @@ static void mpc83xx_setup_phy(struct ehci_hcd *ehci, ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]); } -static void mpc83xx_usb_setup(struct usb_hcd *hcd) +static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) { - struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct usb_hcd *hcd = ehci_to_hcd(ehci); struct fsl_usb2_platform_data *pdata; void __iomem *non_ehci = hcd->regs; u32 temp; - pdata = - (struct fsl_usb2_platform_data *)hcd->self.controller-> - platform_data; + pdata = hcd->self.controller->platform_data; + /* Enable PHY interface in the control reg. */ - temp = in_be32(non_ehci + FSL_SOC_USB_CTRL); - out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004); - out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b); + if (pdata->have_sysif_regs) { + temp = in_be32(non_ehci + FSL_SOC_USB_CTRL); + out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004); + out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b); + } #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) /* @@ -214,7 +250,7 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd) if ((pdata->operating_mode == FSL_USB2_DR_HOST) || (pdata->operating_mode == FSL_USB2_DR_OTG)) - mpc83xx_setup_phy(ehci, pdata->phy_mode, 0); + ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0); if (pdata->operating_mode == FSL_USB2_MPH_HOST) { unsigned int chip, rev, svr; @@ -228,25 +264,27 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd) ehci->has_fsl_port_bug = 1; if (pdata->port_enables & FSL_USB2_PORT0_ENABLED) - mpc83xx_setup_phy(ehci, pdata->phy_mode, 0); + ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0); if (pdata->port_enables & FSL_USB2_PORT1_ENABLED) - mpc83xx_setup_phy(ehci, pdata->phy_mode, 1); + ehci_fsl_setup_phy(ehci, pdata->phy_mode, 1); } + if (pdata->have_sysif_regs) { #ifdef CONFIG_PPC_85xx - out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008); - out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080); + out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008); + out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080); #else - out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c); - out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040); + out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c); + out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040); #endif - out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001); + out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001); + } } /* called after powerup, by probe or system-pm "wakeup" */ static int ehci_fsl_reinit(struct ehci_hcd *ehci) { - mpc83xx_usb_setup(ehci_to_hcd(ehci)); + ehci_fsl_usb_setup(ehci); ehci_port_power(ehci, 0); return 0; @@ -257,6 +295,11 @@ static int ehci_fsl_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; + struct fsl_usb2_platform_data *pdata; + + pdata = hcd->self.controller->platform_data; + ehci->big_endian_desc = pdata->big_endian_desc; + ehci->big_endian_mmio = pdata->big_endian_mmio; /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; @@ -370,7 +413,7 @@ static const struct hc_driver ehci_fsl_hc_driver = { * generic hardware linkage */ .irq = ehci_irq, - .flags = HCD_USB2, + .flags = HCD_USB2 | HCD_MEMORY, /* * basic lifecycle operations diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h index eb537aa54610..2c8353795226 100644 --- a/drivers/usb/host/ehci-fsl.h +++ b/drivers/usb/host/ehci-fsl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2005 freescale semiconductor +/* Copyright (C) 2005-2010 Freescale Semiconductor, Inc. * Copyright (c) 2005 MontaVista Software * * This program is free software; you can redistribute it and/or modify it @@ -19,6 +19,9 @@ #define _EHCI_FSL_H /* offsets for the non-ehci registers in the FSL SOC USB controller */ +#define FSL_SOC_USB_ID 0x0 +#define ID_MSK 0x3f +#define NID_MSK 0x3f00 #define FSL_SOC_USB_ULPIVP 0x170 #define FSL_SOC_USB_PORTSC1 0x184 #define PORT_PTS_MSK (3<<30) @@ -27,6 +30,14 @@ #define PORT_PTS_SERIAL (3<<30) #define PORT_PTS_PTW (1<<28) #define FSL_SOC_USB_PORTSC2 0x188 + +#define FSL_SOC_USB_USBGENCTRL 0x200 +#define USBGENCTRL_PPP (1 << 3) +#define USBGENCTRL_PFP (1 << 2) +#define FSL_SOC_USB_ISIPHYCTRL 0x204 +#define ISIPHYCTRL_PXE (1) +#define ISIPHYCTRL_PHYE (1 << 4) + #define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */ #define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */ #define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */ diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 1f3f01eacaf0..d36e4e75e08d 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -40,7 +40,7 @@ static inline void ehci_qtd_init(struct ehci_hcd *ehci, struct ehci_qtd *qtd, { memset (qtd, 0, sizeof *qtd); qtd->qtd_dma = dma; - qtd->hw_token = cpu_to_le32 (QTD_STS_HALT); + qtd->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT); qtd->hw_next = EHCI_LIST_END(ehci); qtd->hw_alt_next = EHCI_LIST_END(ehci); INIT_LIST_HEAD (&qtd->qtd_list); diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index 12db5d5cb0bc..574b99ea0700 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -15,6 +15,7 @@ #include #include #include +#include struct fsl_usb2_dev_data { char *dr_mode; /* controller mode */ @@ -153,6 +154,12 @@ static int __devinit fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev) pdata->operating_mode = FSL_USB2_MPH_HOST; } else { + if (of_get_property(np, "fsl,invert-drvvbus", NULL)) + pdata->invert_drvvbus = 1; + + if (of_get_property(np, "fsl,invert-pwr-fault", NULL)) + pdata->invert_pwr_fault = 1; + /* setup mode selected in the device tree */ pdata->operating_mode = dev_data->op_mode; } @@ -186,9 +193,91 @@ static int __devexit fsl_usb2_mph_dr_of_remove(struct platform_device *ofdev) return 0; } +#ifdef CONFIG_PPC_MPC512x + +#define USBGENCTRL 0x200 /* NOTE: big endian */ +#define GC_WU_INT_CLR (1 << 5) /* Wakeup int clear */ +#define GC_ULPI_SEL (1 << 4) /* ULPI i/f select (usb0 only)*/ +#define GC_PPP (1 << 3) /* Inv. Port Power Polarity */ +#define GC_PFP (1 << 2) /* Inv. Power Fault Polarity */ +#define GC_WU_ULPI_EN (1 << 1) /* Wakeup on ULPI event */ +#define GC_WU_IE (1 << 1) /* Wakeup interrupt enable */ + +#define ISIPHYCTRL 0x204 /* NOTE: big endian */ +#define PHYCTRL_PHYE (1 << 4) /* On-chip UTMI PHY enable */ +#define PHYCTRL_BSENH (1 << 3) /* Bit Stuff Enable High */ +#define PHYCTRL_BSEN (1 << 2) /* Bit Stuff Enable */ +#define PHYCTRL_LSFE (1 << 1) /* Line State Filter Enable */ +#define PHYCTRL_PXE (1 << 0) /* PHY oscillator enable */ + +int fsl_usb2_mpc5121_init(struct platform_device *pdev) +{ + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + struct clk *clk; + char clk_name[10]; + int base, clk_num; + + base = pdev->resource->start & 0xf000; + if (base == 0x3000) + clk_num = 1; + else if (base == 0x4000) + clk_num = 2; + else + return -ENODEV; + + snprintf(clk_name, sizeof(clk_name), "usb%d_clk", clk_num); + clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get clk\n"); + return PTR_ERR(clk); + } + + clk_enable(clk); + pdata->clk = clk; + + if (pdata->phy_mode == FSL_USB2_PHY_UTMI_WIDE) { + u32 reg = 0; + + if (pdata->invert_drvvbus) + reg |= GC_PPP; + + if (pdata->invert_pwr_fault) + reg |= GC_PFP; + + out_be32(pdata->regs + ISIPHYCTRL, PHYCTRL_PHYE | PHYCTRL_PXE); + out_be32(pdata->regs + USBGENCTRL, reg); + } + return 0; +} + +static void fsl_usb2_mpc5121_exit(struct platform_device *pdev) +{ + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + + pdata->regs = NULL; + + if (pdata->clk) { + clk_disable(pdata->clk); + clk_put(pdata->clk); + } +} + +struct fsl_usb2_platform_data fsl_usb2_mpc5121_pd = { + .big_endian_desc = 1, + .big_endian_mmio = 1, + .es = 1, + .le_setup_buf = 1, + .init = fsl_usb2_mpc5121_init, + .exit = fsl_usb2_mpc5121_exit, +}; +#endif /* CONFIG_PPC_MPC512x */ + static const struct of_device_id fsl_usb2_mph_dr_of_match[] = { { .compatible = "fsl-usb2-mph", }, { .compatible = "fsl-usb2-dr", }, +#ifdef CONFIG_PPC_MPC512x + { .compatible = "fsl,mpc5121-usb2-dr", .data = &fsl_usb2_mpc5121_pd, }, +#endif {}, }; diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 28e33fea5107..d5f9a7431bd0 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -58,11 +58,26 @@ enum fsl_usb2_phy_modes { FSL_USB2_PHY_SERIAL, }; +struct clk; +struct platform_device; + struct fsl_usb2_platform_data { /* board specific information */ enum fsl_usb2_operating_modes operating_mode; enum fsl_usb2_phy_modes phy_mode; unsigned int port_enables; + + int (*init)(struct platform_device *); + void (*exit)(struct platform_device *); + void __iomem *regs; /* ioremap'd register base */ + struct clk *clk; + unsigned big_endian_mmio:1; + unsigned big_endian_desc:1; + unsigned es:1; /* need USBMODE:ES */ + unsigned le_setup_buf:1; + unsigned have_sysif_regs:1; + unsigned invert_drvvbus:1; + unsigned invert_pwr_fault:1; }; /* Flags in fsl_usb2_mph_platform_data */ -- cgit v1.2.3-70-g09d2 From 69194aba3d998af0a51efdf1bde9ce6642b8ab76 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 22 Oct 2010 20:50:50 -0200 Subject: [media] Documentation/video4linux/CARDLIST.[cx88|saa7134] Fix a few troubles at the card entries Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.cx88 | 1 + Documentation/video4linux/CARDLIST.saa7134 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88 index f2510541373b..42517d9121de 100644 --- a/Documentation/video4linux/CARDLIST.cx88 +++ b/Documentation/video4linux/CARDLIST.cx88 @@ -83,3 +83,4 @@ 82 -> WinFast DTV2000 H rev. J [107d:6f2b] 83 -> Prof 7301 DVB-S/S2 [b034:3034] 84 -> Samsung SMT 7020 DVB-S [18ac:dc00,18ac:dccd] + 85 -> Twinhan VP-1027 DVB-S [1822:0023] diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 4000c29fcfb6..8d9afc7d8014 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -126,7 +126,7 @@ 125 -> Beholder BeholdTV 409 [0000:4090] 126 -> Beholder BeholdTV 505 FM [5ace:5050] 127 -> Beholder BeholdTV 507 FM / BeholdTV 509 FM [5ace:5070,5ace:5090] -128 -> Beholder BeholdTV Columbus TVFM [0000:5201] +128 -> Beholder BeholdTV Columbus TV/FM [0000:5201] 129 -> Beholder BeholdTV 607 FM [5ace:6070] 130 -> Beholder BeholdTV M6 [5ace:6190] 131 -> Twinhan Hybrid DTV-DVB 3056 PCI [1822:0022] -- cgit v1.2.3-70-g09d2 From 8298f2f810b988dccfa0ab51cd874e107514c036 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Wed, 20 Oct 2010 07:55:33 -0300 Subject: [media] Support for Elgato Video Capture This patch allows this device successfully to show video, at least from its composite input. I have no information about the true hardware contents of this device and so this patch is based solely on fiddling with things until it worked. The chip appears to be em2860, and the closest device with equivalent inputs is the Typhoon DVD Maker. Copying the settings for that device appears to do the trick. That's what this patch does. [mchehab@redhat.com: update CARDLIST.em28xx accordingly, via script] Signed-off-by: Adrian Taylor Reviewed-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 3 ++- drivers/media/video/em28xx/em28xx-cards.c | 18 ++++++++++++++++++ drivers/media/video/em28xx/em28xx.h | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index 5c568757c301..ac2616a62fc3 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -31,6 +31,7 @@ 30 -> Videology 20K14XUSB USB2.0 (em2820/em2840) 31 -> Usbgear VD204v9 (em2821) 32 -> Supercomp USB 2.0 TV (em2821) + 33 -> Elgato Video Capture (em2860) [0fd9:0033] 34 -> Terratec Cinergy A Hybrid XS (em2860) [0ccd:004f] 35 -> Typhoon DVD Maker (em2860) 36 -> NetGMBH Cam (em2860) @@ -45,7 +46,7 @@ 45 -> Pinnacle PCTV DVB-T (em2870) 46 -> Compro, VideoMate U3 (em2870) [185b:2870] 47 -> KWorld DVB-T 305U (em2880) [eb1a:e305] - 48 -> KWorld DVB-T 310U (em2880) [eb1a:e310] + 48 -> KWorld DVB-T 310U (em2880) 49 -> MSI DigiVox A/D (em2880) [eb1a:e310] 50 -> MSI DigiVox A/D II (em2880) [eb1a:e320] 51 -> Terratec Hybrid XS Secam (em2880) [0ccd:004c] diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index f6dbb2148060..a7cec2737d07 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -1660,6 +1660,22 @@ struct em28xx_board em28xx_boards[] = { .gpio = terratec_av350_unmute_gpio, } }, }, + + [EM2860_BOARD_ELGATO_VIDEO_CAPTURE] = { + .name = "Elgato Video Capture", + .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, /* Capture only device */ + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2882_BOARD_EVGA_INDTUBE] = { .name = "Evga inDtube", .tuner_type = TUNER_XC2028, @@ -1784,6 +1800,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2860_BOARD_TERRATEC_AV350 }, { USB_DEVICE(0x0ccd, 0x0096), .driver_info = EM2860_BOARD_TERRATEC_GRABBY }, + { USB_DEVICE(0x0fd9, 0x0033), + .driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE}, { USB_DEVICE(0x185b, 0x2870), .driver_info = EM2870_BOARD_COMPRO_VIDEOMATE }, { USB_DEVICE(0x185b, 0x2041), diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index adb20eb6d902..6a75e6a4fc21 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -74,6 +74,7 @@ #define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30 #define EM2821_BOARD_USBGEAR_VD204 31 #define EM2821_BOARD_SUPERCOMP_USB_2 32 +#define EM2860_BOARD_ELGATO_VIDEO_CAPTURE 33 #define EM2860_BOARD_TERRATEC_HYBRID_XS 34 #define EM2860_BOARD_TYPHOON_DVD_MAKER 35 #define EM2860_BOARD_NETGMBH_CAM 36 -- cgit v1.2.3-70-g09d2 From ab599a6d474e0dd4db3557a46dd15cff6e126d0b Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sat, 16 Oct 2010 16:44:43 -0300 Subject: [media] lmedm04: driver for DM04/QQBOX updated to version 1.60 These include -later kill of usb_buffer to avoid kernel crash on hot unplugging. -DiSEqC functions. -LNB Power switch -Faster channel change. -support for LG tuner on LME2510C. -firmware switching for LG tuner. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- Documentation/dvb/lmedm04.txt | 9 +- drivers/media/dvb/dvb-usb/Kconfig | 1 - drivers/media/dvb/dvb-usb/lmedm04.c | 292 +++++++++++++++++++++++++++--------- drivers/media/dvb/dvb-usb/lmedm04.h | 44 ++---- 4 files changed, 243 insertions(+), 103 deletions(-) (limited to 'Documentation') diff --git a/Documentation/dvb/lmedm04.txt b/Documentation/dvb/lmedm04.txt index 4bde457fafc4..e175784b89bf 100644 --- a/Documentation/dvb/lmedm04.txt +++ b/Documentation/dvb/lmedm04.txt @@ -45,10 +45,13 @@ and run Other LG firmware can be extracted manually from US280D.sys only found in windows/system32/driver. -However, this firmware does not run very well under Windows -or with the Linux driver. -dd if=US280D.sys ibs=1 skip=36856 count=3976 of=dvb-usb-lme2510-lg.fw +dd if=US280D.sys ibs=1 skip=42616 count=3668 of=dvb-usb-lme2510-lg.fw + +for DM04 LME2510C (LG Tuner) +--------------------------- + +dd if=US280D.sys ibs=1 skip=35200 count=3850 of=dvb-usb-lme2510c-lg.fw --------------------------------------------------------------------- diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index ce60c1e46e15..2525d3b3c88d 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -356,6 +356,5 @@ config DVB_USB_LME2510 select DVB_TDA826X if !DVB_FE_CUSTOMISE select DVB_STV0288 if !DVB_FE_CUSTOMISE select DVB_IX2505V if !DVB_FE_CUSTOMISE - depends on IR_CORE help Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 . diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c index d5374ac075d3..d939fbbf9fe6 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.c +++ b/drivers/media/dvb/dvb-usb/lmedm04.c @@ -1,14 +1,17 @@ /* DVB USB compliant linux driver for * * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395 - * LME2510 + LGTDQT-P001F + * LME2510C + LG TDQY-P001F + * LME2510 + LG TDQY-P001F * * MVB7395 (LME2510C+SHARP:BS2F7HZ7395) * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V) * - * MV001F (LME2510 +LGTDQY-P001F) + * MV001F (LME2510+LGTDQY-P001F) * LG TDQY - P001F =(TDA8263 + TDA10086H) * + * MVB0001F (LME2510C+LGTDQT-P001F) + * * For firmware see Documentation/dvb/lmedm04.txt * * I2C addresses: @@ -21,7 +24,6 @@ * ***Please Note*** * There are other variants of the DM04 * ***NOT SUPPORTED*** - * MVB0001F (LME2510C+LGTDQT-P001F) * MV0194 (LME2510+SHARP0194) * MVB0194 (LME2510C+SHARP0194) * @@ -51,10 +53,7 @@ * LME2510: Non Intel USB chipsets fail to maintain High Speed on * Boot or Hot Plug. * - * DiSEqC functions are not fully supported in this driver. The main - * reason is the frontend is cut off during streaming. Allowing frontend - * access will stall the driver. Applications that attempt to this, the - * commands are ignored. + * QQbox suffers from noise on LNB voltage. * * PID functions have been removed from this driver version due to * problems with different firmware and application versions. @@ -91,9 +90,14 @@ module_param_named(debug, dvb_usb_lme2510_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." DVB_USB_DEBUG_STATUS); +static int dvb_usb_lme2510_firmware; +module_param_named(firmware, dvb_usb_lme2510_firmware, int, 0644); +MODULE_PARM_DESC(firmware, "set default firmware 0=Sharp7395 1=LG"); + + DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -#define TUNER_LG 0x1 -#define TUNER_S7395 0x2 +#define TUNER_LG 0x1 +#define TUNER_S7395 0x2 struct lme2510_state { u8 id; @@ -107,6 +111,8 @@ struct lme2510_state { u8 i2c_tuner_gate_w; u8 i2c_tuner_gate_r; u8 i2c_tuner_addr; + u8 stream_on; + u8 one_tune; void *buffer; struct urb *lme_urb; void *usb_buffer; @@ -117,6 +123,7 @@ static int lme2510_bulk_write(struct usb_device *dev, u8 *snd, int len, u8 pipe) { int ret, actual_l; + ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe), snd, len , &actual_l, 500); return ret; @@ -151,22 +158,21 @@ static int lme2510_usb_talk(struct dvb_usb_device *d, /* the read/write capped at 512 */ memcpy(buff, wbuf, (wlen > 512) ? 512 : wlen); - ret = mutex_lock_interruptible(&d->usb_mutex); if (ret < 0) return -EAGAIN; ret |= usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, 0x01)); - msleep(5); - ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x1); - msleep(5); - ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x1)); + ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x01); + + msleep(12); + + ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x01)); - msleep(5); ret |= lme2510_bulk_read(d->udev, buff, (rlen > 512) ? - 512 : rlen , 0x1); + 512 : rlen , 0x01); if (rlen > 0) memcpy(rbuf, buff, rlen); @@ -176,6 +182,18 @@ static int lme2510_usb_talk(struct dvb_usb_device *d, return (ret < 0) ? -ENODEV : 0; } +static int lme2510_usb_talk_restart(struct dvb_usb_device *d, + u8 *wbuf, int wlen, u8 *rbuf, int rlen) { + static u8 stream_on[] = LME_ST_ON_W; + int ret; + u8 rbuff[10]; + /*Send Normal Command*/ + ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); + /*Restart Stream Command*/ + ret |= lme2510_usb_talk(d, stream_on, sizeof(stream_on), + rbuff, sizeof(rbuff)); + return ret; +} static int lme2510_remote_keypress(struct dvb_usb_adapter *adap, u16 keypress) { struct dvb_usb_device *d = adap->dev; @@ -213,7 +231,6 @@ static void lme2510_int_response(struct urb *lme_urb) offset = ((lme_urb->actual_length/8) > 4) ? 4 : (lme_urb->actual_length/8) ; - for (i = 0; i < offset; ++i) { ibuf = (u8 *)&rbuf[i*8]; deb_info(5, "INT O/S C =%02x C/O=%02x Type =%02x%02x", @@ -228,7 +245,8 @@ static void lme2510_int_response(struct urb *lme_urb) case 0xbb: switch (st->tuner_config) { case TUNER_LG: - st->signal_lock = ibuf[2]; + if (ibuf[2] > 0) + st->signal_lock = ibuf[2]; st->signal_level = ibuf[4]; st->signal_sn = ibuf[3]; st->time_key = ibuf[7]; @@ -237,7 +255,7 @@ static void lme2510_int_response(struct urb *lme_urb) /* Tweak for earlier firmware*/ if (ibuf[1] == 0x03) { st->signal_level = ibuf[3]; - st->signal_sn = ibuf[2]; + st->signal_sn = ibuf[4]; } else { st->signal_level = ibuf[4]; st->signal_sn = ibuf[5]; @@ -295,6 +313,7 @@ static int lme2510_return_status(struct usb_device *dev) { int ret = 0; u8 data[10] = {0}; + ret |= usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x06, 0x80, 0x0302, 0x00, data, 0x0006, 200); info("Firmware Status: %x (%x)", ret , data[2]); @@ -308,25 +327,48 @@ static int lme2510_msg(struct dvb_usb_device *d, int ret = 0; struct lme2510_state *st = d->priv; - if (st->i2c_talk_onoff == 1) { - if ((wbuf[2] == 0x1c) & (wbuf[3] == 0x0e)) - msleep(80); /*take your time when waiting for tune*/ - - if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) return -EAGAIN; + if (st->i2c_talk_onoff == 1) { + ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); - mutex_unlock(&d->i2c_mutex); switch (st->tuner_config) { + case TUNER_LG: + if (wbuf[2] == 0x1c) { + if (wbuf[3] == 0x0e) { + st->signal_lock = rbuf[1]; + if ((st->stream_on & 1) && + (st->signal_lock & 0x10)) { + lme2510_usb_talk_restart(d, + wbuf, wlen, rbuf, rlen); + st->i2c_talk_onoff = 0; + } + msleep(80); + } + } + break; case TUNER_S7395: - if (wbuf[3] == 0x24) - st->signal_lock = rbuf[1]; + if (wbuf[2] == 0xd0) { + if (wbuf[3] == 0x24) { + st->signal_lock = rbuf[1]; + if ((st->stream_on & 1) && + (st->signal_lock & 0x8)) { + lme2510_usb_talk_restart(d, + wbuf, wlen, rbuf, rlen); + st->i2c_talk_onoff = 0; + } + } + if ((wbuf[3] != 0x6) & (wbuf[3] != 0x5)) + msleep(5); + + + } break; default: break; } - } else { switch (st->tuner_config) { case TUNER_LG: @@ -343,6 +385,17 @@ static int lme2510_msg(struct dvb_usb_device *d, rbuf[0] = 0x55; rbuf[1] = st->signal_sn; break; + /*DiSEqC functions as per TDA10086*/ + case 0x36: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + if (wbuf[2] == 0x1c) + lme2510_usb_talk_restart(d, + wbuf, wlen, rbuf, rlen); default: break; } @@ -362,7 +415,31 @@ static int lme2510_msg(struct dvb_usb_device *d, rbuf[0] = 0x55; rbuf[1] = (st->signal_level & 0x80) ? 0 : st->signal_lock; + break; + case 0x6: + if (wbuf[2] == 0xd0) + lme2510_usb_talk(d, + wbuf, wlen, rbuf, rlen); + break; + case 0x1: + if (st->one_tune > 0) + break; + st->one_tune++; + st->i2c_talk_onoff = 1; + /*DiSEqC functions as per STV0288*/ + case 0x5: + case 0x7: + case 0x8: + case 0x9: + case 0xa: + case 0xb: + if (wbuf[2] == 0xd0) + lme2510_usb_talk_restart(d, + wbuf, wlen, rbuf, rlen); + break; default: + rbuf[0] = 0x55; + rbuf[1] = 0x00; break; } break; @@ -376,6 +453,8 @@ static int lme2510_msg(struct dvb_usb_device *d, } + mutex_unlock(&d->i2c_mutex); + return ret; } @@ -469,29 +548,34 @@ static int lme2510_identify_state(struct usb_device *udev, static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) { struct lme2510_state *st = adap->dev->priv; - static u8 reset[] = LME_RESET; static u8 stream_on[] = LME_ST_ON_W; static u8 clear_reg_3[] = LME_CLEAR_PID; static u8 rbuf[1]; + static u8 timeout; int ret = 0, len = 2, rlen = sizeof(rbuf); deb_info(1, "STM (%02x)", onoff); if (onoff == 1) { st->i2c_talk_onoff = 0; - msleep(400); /* give enough time for i2c to stop */ + timeout = 0; + /* wait for i2C to be free */ + while (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) { + timeout++; + if (timeout > 5) + return -ENODEV; + } + msleep(100); ret |= lme2510_usb_talk(adap->dev, stream_on, len, rbuf, rlen); + st->stream_on = 1; + st->one_tune = 0; + mutex_unlock(&adap->dev->i2c_mutex); } else { deb_info(1, "STM Steam Off"); - if (st->tuner_config == TUNER_LG) - ret |= lme2510_usb_talk(adap->dev, clear_reg_3, + ret |= lme2510_usb_talk(adap->dev, clear_reg_3, sizeof(clear_reg_3), rbuf, rlen); - else - ret |= lme2510_usb_talk(adap->dev, - reset, sizeof(reset), rbuf, rlen); - - msleep(400); + st->stream_on = 0; st->i2c_talk_onoff = 1; } @@ -604,6 +688,63 @@ static int lme2510_download_firmware(struct usb_device *dev, return (ret < 0) ? -ENODEV : 0; } +/* Default firmware for LME2510C */ +const char lme_firmware[50] = "dvb-usb-lme2510c-s7395.fw"; + +static void lme_coldreset(struct usb_device *dev) +{ + int ret = 0, len_in; + u8 data[512] = {0}; + + data[0] = 0x0a; + len_in = 1; + info("FRM Firmware Cold Reset"); + ret |= lme2510_bulk_write(dev, data , len_in, 1); /*Cold Resetting*/ + ret |= lme2510_bulk_read(dev, data, len_in, 1); + return; +} + +static void lme_firmware_switch(struct usb_device *udev, int cold) +{ + const struct firmware *fw = NULL; + char lme2510c_s7395[] = "dvb-usb-lme2510c-s7395.fw"; + char lme2510c_lg[] = "dvb-usb-lme2510c-lg.fw"; + char *firm_msg[] = {"Loading", "Switching to"}; + int ret; + + if (udev->descriptor.idProduct == 0x1122) + return; + + switch (dvb_usb_lme2510_firmware) { + case 0: + default: + memcpy(&lme_firmware, lme2510c_s7395, sizeof(lme2510c_s7395)); + ret = request_firmware(&fw, lme_firmware, &udev->dev); + if (ret == 0) { + info("FRM %s S7395 Firmware", firm_msg[cold]); + break; + } + if (cold == 0) + dvb_usb_lme2510_firmware = 1; + else + cold = 0; + case 1: + memcpy(&lme_firmware, lme2510c_lg, sizeof(lme2510c_lg)); + ret = request_firmware(&fw, lme_firmware, &udev->dev); + if (ret == 0) { + info("FRM %s LG Firmware", firm_msg[cold]); + break; + } + info("FRM No Firmware Found - please install"); + dvb_usb_lme2510_firmware = 0; + cold = 0; + break; + } + release_firmware(fw); + if (cold) + lme_coldreset(udev); + return; +} static int lme2510_kill_urb(struct usb_data_stream *stream) { @@ -638,7 +779,6 @@ static struct ix2505v_config lme_tuner = { .tuner_chargepump = 0x3, }; - static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { @@ -646,35 +786,28 @@ static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, struct lme2510_state *st = adap->dev->priv; static u8 voltage_low[] = LME_VOLTAGE_L; static u8 voltage_high[] = LME_VOLTAGE_H; - static u8 reset[] = LME_RESET; - static u8 clear_reg_3[] = LME_CLEAR_PID; + static u8 lnb_on[] = LNB_ON; + static u8 lnb_off[] = LNB_OFF; static u8 rbuf[1]; int ret = 0, len = 3, rlen = 1; - msleep(100); - - if (st->tuner_config == TUNER_LG) - ret |= lme2510_usb_talk(adap->dev, clear_reg_3, - sizeof(clear_reg_3), rbuf, rlen); - else - ret |= lme2510_usb_talk(adap->dev, - reset, sizeof(reset), rbuf, rlen); - - /*always check & stop streaming*/ - lme2510_kill_urb(&adap->stream); - adap->feedcount = 0; + if (st->stream_on == 1) + return 0; - switch (voltage) { + ret |= lme2510_usb_talk(adap->dev, lnb_on, len, rbuf, rlen); - case SEC_VOLTAGE_18: - ret |= lme2510_usb_talk(adap->dev, - voltage_high, len, rbuf, rlen); + switch (voltage) { + case SEC_VOLTAGE_18: + ret |= lme2510_usb_talk(adap->dev, + voltage_high, len, rbuf, rlen); break; - case SEC_VOLTAGE_OFF: - case SEC_VOLTAGE_13: - default: - ret |= lme2510_usb_talk(adap->dev, + case SEC_VOLTAGE_OFF: + ret |= lme2510_usb_talk(adap->dev, + lnb_off, len, rbuf, rlen); + case SEC_VOLTAGE_13: + default: + ret |= lme2510_usb_talk(adap->dev, voltage_low, len, rbuf, rlen); break; @@ -714,6 +847,10 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) &adap->dev->i2c_adap, 1)) { info("TUN TDA8263 Found"); st->tuner_config = TUNER_LG; + if (dvb_usb_lme2510_firmware != 1) { + dvb_usb_lme2510_firmware = 1; + lme_firmware_switch(adap->dev->udev, 1); + } return 0; } kfree(adap->fe); @@ -735,6 +872,10 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) &adap->dev->i2c_adap)) { st->tuner_config = TUNER_S7395; info("TUN Sharp IX2505V silicon tuner"); + if (dvb_usb_lme2510_firmware != 0) { + dvb_usb_lme2510_firmware = 0; + lme_firmware_switch(adap->dev->udev, 1); + } return 0; } kfree(adap->fe); @@ -772,6 +913,8 @@ static int lme2510_probe(struct usb_interface *intf, return -ENODEV; } + lme_firmware_switch(udev, 0); + if (0 == dvb_usb_device_init(intf, &lme2510_properties, THIS_MODULE, NULL, adapter_nr)) { info("DEV registering device driver"); @@ -839,7 +982,7 @@ static struct dvb_usb_device_properties lme2510c_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, .download_firmware = lme2510_download_firmware, - .firmware = "dvb-usb-lme2510c-s7395.fw", + .firmware = lme_firmware, .size_of_priv = sizeof(struct lme2510_state), .num_adapters = 1, .adapter = { @@ -849,7 +992,7 @@ static struct dvb_usb_device_properties lme2510c_properties = { /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 8, + .count = 10, .endpoint = 0x8, .u = { .bulk = { @@ -872,15 +1015,23 @@ static struct dvb_usb_device_properties lme2510c_properties = { } }; -void lme2510_exit_int(struct dvb_usb_device *d) +void *lme2510_exit_int(struct dvb_usb_device *d) { struct lme2510_state *st = d->priv; + struct dvb_usb_adapter *adap = &d->adapter[0]; + void *buffer = NULL; + + if (adap != NULL) { + lme2510_kill_urb(&adap->stream); + adap->feedcount = 0; + } + if (st->lme_urb != NULL) { - st->i2c_talk_onoff = 0; + st->i2c_talk_onoff = 1; st->signal_lock = 0; st->signal_level = 0; st->signal_sn = 0; - kfree(st->usb_buffer); + buffer = st->usb_buffer; usb_kill_urb(st->lme_urb); usb_free_coherent(d->udev, 5000, st->buffer, st->lme_urb->transfer_dma); @@ -888,18 +1039,19 @@ void lme2510_exit_int(struct dvb_usb_device *d) ir_input_unregister(d->rc_input_dev); info("Remote Stopped"); } - return; + return buffer; } void lme2510_exit(struct usb_interface *intf) { struct dvb_usb_device *d = usb_get_intfdata(intf); + void *usb_buffer; + if (d != NULL) { - d->adapter[0].feedcount = 0; - lme2510_exit_int(d); + usb_buffer = lme2510_exit_int(d); dvb_usb_device_exit(intf); + kfree(usb_buffer); } - } static struct usb_driver lme2510_driver = { @@ -932,5 +1084,5 @@ module_exit(lme2510_module_exit); MODULE_AUTHOR("Malcolm Priestley "); MODULE_DESCRIPTION("LM2510(C) DVB-S USB2.0"); -MODULE_VERSION("1.4"); +MODULE_VERSION("1.60"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/lmedm04.h b/drivers/media/dvb/dvb-usb/lmedm04.h index 5a66c7ec4511..e6af16c1e3e5 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.h +++ b/drivers/media/dvb/dvb-usb/lmedm04.h @@ -1,14 +1,16 @@ /* DVB USB compliant linux driver for * * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395 - * LME2510C + LGTDQT-P001F + * LME2510C + LG TDQY-P001F + * LME2510 + LG TDQY-P001F * * MVB7395 (LME2510C+SHARP:BS2F7HZ7395) * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V) * - * MVB0001F (LME2510C+LGTDQT-P001F) + * MVB001F (LME2510+LGTDQT-P001F) * LG TDQY - P001F =(TDA8263 + TDA10086H) * + * MVB0001F (LME2510C+LGTDQT-P001F) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -35,35 +37,33 @@ * end byte -1 = 20 * end byte = clear pid always a0, other wise 9c, 9a ?? * - * RESET (also clears PID filter) - * 3a 01 00 */ #define LME_ST_ON_W {0x06, 0x00} -#define LME_RESET {0x3a, 0x01, 0x00} #define LME_CLEAR_PID {0x03, 0x02, 0x20, 0xa0} - -/* LME Power Control +/* LNB Voltage * 07 XX XX - * offset 1 = 01 Power? my device cannot be powered down + * offset 1 = 01 * offset 2 = 00=Voltage low 01=Voltage high + * + * LNB Power + * 03 01 XX + * offset 2 = 00=ON 01=OFF */ #define LME_VOLTAGE_L {0x07, 0x01, 0x00} #define LME_VOLTAGE_H {0x07, 0x01, 0x01} - +#define LNB_ON {0x3a, 0x01, 0x00} +#define LNB_OFF {0x3a, 0x01, 0x01} /* Initial stv0288 settings for 7395 Frontend */ static u8 s7395_inittab[] = { - 0x00, 0x11, 0x01, 0x15, 0x02, 0x20, - 0x03, 0x8e, - 0x04, 0x8e, + 0x03, 0xa0, + 0x04, 0xa0, 0x05, 0x12, - 0x06, 0xff, - 0x07, 0x20, - 0x08, 0x00, + 0x06, 0x00, 0x09, 0x00, 0x0a, 0x04, 0x0b, 0x00, @@ -71,7 +71,6 @@ static u8 s7395_inittab[] = { 0x0d, 0x00, 0x0e, 0xc1, 0x0f, 0x54, - 0x10, 0x40, 0x11, 0x7a, 0x12, 0x03, 0x13, 0x48, @@ -84,23 +83,15 @@ static u8 s7395_inittab[] = { 0x1a, 0x88, 0x1b, 0x8f, 0x1c, 0xf0, - 0x1e, 0x80, 0x20, 0x0b, 0x21, 0x54, 0x22, 0xff, 0x23, 0x01, - 0x24, 0x9a, - 0x25, 0x7f, - 0x26, 0x00, - 0x27, 0x00, 0x28, 0x46, 0x29, 0x66, 0x2a, 0x90, 0x2b, 0xfa, 0x2c, 0xd9, - 0x2d, 0x02, - 0x2e, 0xb1, - 0x2f, 0x00, 0x30, 0x0, 0x31, 0x1e, 0x32, 0x14, @@ -115,8 +106,6 @@ static u8 s7395_inittab[] = { 0x3b, 0x13, 0x3c, 0x11, 0x3d, 0x30, - 0x3e, 0x00, - 0x3f, 0x00, 0x40, 0x63, 0x41, 0x04, 0x42, 0x60, @@ -126,8 +115,6 @@ static u8 s7395_inittab[] = { 0x46, 0x00, 0x47, 0x00, 0x4a, 0x00, - 0x4b, 0xd1, - 0x4c, 0x33, 0x50, 0x12, 0x51, 0x36, 0x52, 0x21, @@ -183,5 +170,4 @@ static u8 s7395_inittab[] = { 0xf2, 0xc0, 0xff, 0xff, }; - #endif -- cgit v1.2.3-70-g09d2 From eb8942e9b6150d3cc89640f1fb243ca9626a1d14 Mon Sep 17 00:00:00 2001 From: Matti Aaltonen Date: Mon, 18 Oct 2010 10:52:37 -0300 Subject: [media] Documentation: v4l: Add hw_seek spacing and two TUNER_RDS_CAP flags Add a couple of words about the spacing field in the HW seek struct, also a few words about the new RDS tuner capability flags V4L2_TUNER_CAP_RDS_BLOCK-IO and V4L2_TUNER_CAP_RDS_CONTROLS. Signed-off-by: Matti J. Aaltonen Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/dev-rds.xml | 68 ++++++++++++++++------ .../DocBook/v4l/vidioc-s-hw-freq-seek.xml | 10 +++- 2 files changed, 59 insertions(+), 19 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/dev-rds.xml b/Documentation/DocBook/v4l/dev-rds.xml index 0869d701b1e5..360d2737e649 100644 --- a/Documentation/DocBook/v4l/dev-rds.xml +++ b/Documentation/DocBook/v4l/dev-rds.xml @@ -3,15 +3,16 @@ The Radio Data System transmits supplementary information in binary format, for example the station name or travel information, on an inaudible audio subcarrier of a radio program. This -interface is aimed at devices capable of receiving and decoding RDS +interface is aimed at devices capable of receiving and/or transmitting RDS information. For more information see the core RDS standard and the RBDS standard . Note that the RBDS standard as is used in the USA is almost identical -to the RDS standard. Any RDS decoder can also handle RBDS. Only some of the fields -have slightly different meanings. See the RBDS standard for more information. +to the RDS standard. Any RDS decoder/encoder can also handle RBDS. Only some of the +fields have slightly different meanings. See the RBDS standard for more +information. The RBDS standard also specifies support for MMBS (Modified Mobile Search). This is a proprietary format which seems to be discontinued. The RDS interface does not @@ -21,16 +22,25 @@ be needed, then please contact the linux-media mailing list: &v4l-ml;.
Querying Capabilities - Devices supporting the RDS capturing API -set the V4L2_CAP_RDS_CAPTURE flag in + Devices supporting the RDS capturing API set +the V4L2_CAP_RDS_CAPTURE flag in the capabilities field of &v4l2-capability; -returned by the &VIDIOC-QUERYCAP; ioctl. -Any tuner that supports RDS will set the -V4L2_TUNER_CAP_RDS flag in the capability -field of &v4l2-tuner;. -Whether an RDS signal is present can be detected by looking at -the rxsubchans field of &v4l2-tuner;: the -V4L2_TUNER_SUB_RDS will be set if RDS data was detected. +returned by the &VIDIOC-QUERYCAP; ioctl. Any tuner that supports RDS +will set the V4L2_TUNER_CAP_RDS flag in +the capability field of &v4l2-tuner;. If +the driver only passes RDS blocks without interpreting the data +the V4L2_TUNER_SUB_RDS_BLOCK_IO flag has to be +set, see Reading RDS data. +For future use the +flag V4L2_TUNER_SUB_RDS_CONTROLS has also been +defined. However, a driver for a radio tuner with this capability does +not yet exist, so if you are planning to write such a driver you +should discuss this on the linux-media mailing list: &v4l-ml;. + + Whether an RDS signal is present can be detected by looking +at the rxsubchans field of &v4l2-tuner;: +the V4L2_TUNER_SUB_RDS will be set if RDS data +was detected. Devices supporting the RDS output API set the V4L2_CAP_RDS_OUTPUT flag in @@ -40,16 +50,31 @@ Any modulator that supports RDS will set the V4L2_TUNER_CAP_RDS flag in the capability field of &v4l2-modulator;. In order to enable the RDS transmission one must set the V4L2_TUNER_SUB_RDS -bit in the txsubchans field of &v4l2-modulator;. - +bit in the txsubchans field of &v4l2-modulator;. +If the driver only passes RDS blocks without interpreting the data +the V4L2_TUNER_SUB_RDS_BLOCK_IO flag has to be set. If the +tuner is capable of handling RDS entities like program identification codes and radio +text, the flag V4L2_TUNER_SUB_RDS_CONTROLS should be set, +see Writing RDS data and +FM Transmitter Control Reference.
-
+
Reading RDS data RDS data can be read from the radio device -with the &func-read; function. The data is packed in groups of three bytes, +with the &func-read; function. The data is packed in groups of three bytes. +
+ +
+ Writing RDS data + + RDS data can be written to the radio device +with the &func-write; function. The data is packed in groups of three bytes, as follows: +
+ +
struct <structname>v4l2_rds_data</structname> @@ -111,48 +136,57 @@ as follows: V4L2_RDS_BLOCK_MSK + 7 Mask for bits 0-2 to get the block ID. V4L2_RDS_BLOCK_A + 0 Block A. V4L2_RDS_BLOCK_B + 1 Block B. V4L2_RDS_BLOCK_C + 2 Block C. V4L2_RDS_BLOCK_D + 3 Block D. V4L2_RDS_BLOCK_C_ALT + 4 Block C'. V4L2_RDS_BLOCK_INVALID + read-only 7 An invalid block. V4L2_RDS_BLOCK_CORRECTED + read-only 0x40 A bit error was detected but corrected. V4L2_RDS_BLOCK_ERROR + read-only 0x80 - An incorrectable error occurred. + An uncorrectable error occurred. diff --git a/Documentation/DocBook/v4l/vidioc-s-hw-freq-seek.xml b/Documentation/DocBook/v4l/vidioc-s-hw-freq-seek.xml index 14b3ec7ed75b..c30dcc4232c0 100644 --- a/Documentation/DocBook/v4l/vidioc-s-hw-freq-seek.xml +++ b/Documentation/DocBook/v4l/vidioc-s-hw-freq-seek.xml @@ -51,7 +51,8 @@ Start a hardware frequency seek from the current frequency. To do this applications initialize the tuner, -type, seek_upward and +type, seek_upward, +spacing and wrap_around fields, and zero out the reserved array of a &v4l2-hw-freq-seek; and call the VIDIOC_S_HW_FREQ_SEEK ioctl with a pointer @@ -89,7 +90,12 @@ field and the &v4l2-tuner; index field. __u32 - reserved[8] + spacing + If non-zero, defines the hardware seek resolution in Hz. The driver selects the nearest value that is supported by the device. If spacing is zero a reasonable default value is used. + + + __u32 + reserved[7] Reserved for future extensions. Drivers and applications must set the array to zero. -- cgit v1.2.3-70-g09d2 From a99e3c515e4e59eb66213baf57137b9ae78a1493 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 25 Feb 2010 14:33:27 -0300 Subject: [media] DocBook/v4l: Add missing formats used on gspca cpia1 and sn9c2028 Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/pixfmt.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml index f4cc0a9e658e..861664dfd6f0 100644 --- a/Documentation/DocBook/v4l/pixfmt.xml +++ b/Documentation/DocBook/v4l/pixfmt.xml @@ -685,6 +685,11 @@ http://www.ivtvdriver.org/The format is documented in the kernel sources in the file Documentation/video4linux/cx2341x/README.hm12 + + V4L2_PIX_FMT_CPIA1 + 'CPIA' + YUV format used by the gspca cpia1 driver. + V4L2_PIX_FMT_SPCA501 'S501' @@ -770,6 +775,11 @@ kernel sources in the file Documentation/video4linux/cx2341x/README.hm 'S920' YUV 4:2:0 format of the gspca sn9c20x driver. + + V4L2_PIX_FMT_SN9C2028 + 'SONX' + Compressed GBRG bayer format of the gspca sn9c2028 driver. + V4L2_PIX_FMT_STV0680 'S680' -- cgit v1.2.3-70-g09d2 From 039aa70218e28a07fdf78220b814cfc4c1c6c3d2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 25 Feb 2010 14:33:27 -0300 Subject: [media] v4l: document new Bayer and monochrome pixel formats Document all four 10-bit Bayer formats, 10-bit monochrome and a missing 8-bit Bayer formats. create mode 100644 Documentation/DocBook/v4l/pixfmt-srggb10.xml create mode 100644 Documentation/DocBook/v4l/pixfmt-srggb8.xml create mode 100644 Documentation/DocBook/v4l/pixfmt-y10.xml [mchehab@redhat.com: remove duplicated symbol for V4L2-PIX-FMT-SGRBG10 and added the corresponding entries at media-entities.tmpl] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media-entities.tmpl | 6 ++ Documentation/DocBook/v4l/pixfmt-srggb10.xml | 90 ++++++++++++++++++++++++++++ Documentation/DocBook/v4l/pixfmt-srggb8.xml | 67 +++++++++++++++++++++ Documentation/DocBook/v4l/pixfmt-y10.xml | 79 ++++++++++++++++++++++++ Documentation/DocBook/v4l/pixfmt.xml | 8 +-- 5 files changed, 245 insertions(+), 5 deletions(-) create mode 100644 Documentation/DocBook/v4l/pixfmt-srggb10.xml create mode 100644 Documentation/DocBook/v4l/pixfmt-srggb8.xml create mode 100644 Documentation/DocBook/v4l/pixfmt-y10.xml (limited to 'Documentation') diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index 6ae97157b1c7..be34dcbe0d90 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -250,6 +250,9 @@ + + + @@ -347,6 +350,9 @@ + + + diff --git a/Documentation/DocBook/v4l/pixfmt-srggb10.xml b/Documentation/DocBook/v4l/pixfmt-srggb10.xml new file mode 100644 index 000000000000..7b274092e60c --- /dev/null +++ b/Documentation/DocBook/v4l/pixfmt-srggb10.xml @@ -0,0 +1,90 @@ + + + V4L2_PIX_FMT_SRGGB10 ('RG10'), + V4L2_PIX_FMT_SGRBG10 ('BA10'), + V4L2_PIX_FMT_SGBRG10 ('GB10'), + V4L2_PIX_FMT_SBGGR10 ('BG10'), + + &manvol; + + + V4L2_PIX_FMT_SRGGB10 + V4L2_PIX_FMT_SGRBG10 + V4L2_PIX_FMT_SGBRG10 + V4L2_PIX_FMT_SBGGR10 + 10-bit Bayer formats expanded to 16 bits + + + Description + + The following four pixel formats are raw sRGB / Bayer formats with +10 bits per colour. Each colour component is stored in a 16-bit word, with 6 +unused high bits filled with zeros. Each n-pixel row contains n/2 green samples +and n/2 blue or red samples, with alternating red and blue rows. Bytes are +stored in memory in little endian order. They are conventionally described +as GRGR... BGBG..., RGRG... GBGB..., etc. Below is an example of one of these +formats + + + <constant>V4L2_PIX_FMT_SBGGR10</constant> 4 × 4 +pixel image + + + Byte Order. + Each cell is one byte, high 6 bits in high bytes are 0. + + + + + + start + 0: + B00low + B00high + G01low + G01high + B02low + B02high + G03low + G03high + + + start + 8: + G10low + G10high + R11low + R11high + G12low + G12high + R13low + R13high + + + start + 16: + B20low + B20high + G21low + G21high + B22low + B22high + G23low + G23high + + + start + 24: + G30low + G30high + R31low + R31high + G32low + G32high + R33low + R33high + + + + + + + + + diff --git a/Documentation/DocBook/v4l/pixfmt-srggb8.xml b/Documentation/DocBook/v4l/pixfmt-srggb8.xml new file mode 100644 index 000000000000..2570e3be3cf1 --- /dev/null +++ b/Documentation/DocBook/v4l/pixfmt-srggb8.xml @@ -0,0 +1,67 @@ + + + V4L2_PIX_FMT_SRGGB8 ('RGGB') + &manvol; + + + V4L2_PIX_FMT_SRGGB8 + Bayer RGB format + + + Description + + This is commonly the native format of digital cameras, +reflecting the arrangement of sensors on the CCD device. Only one red, +green or blue value is given for each pixel. Missing components must +be interpolated from neighbouring pixels. From left to right the first +row consists of a red and green value, the second row of a green and +blue value. This scheme repeats to the right and down for every two +columns and rows. + + + <constant>V4L2_PIX_FMT_SRGGB8</constant> 4 × 4 +pixel image + + + Byte Order. + Each cell is one byte. + + + + + + start + 0: + R00 + G01 + R02 + G03 + + + start + 4: + G10 + B11 + G12 + B13 + + + start + 8: + R20 + G21 + R22 + G23 + + + start + 12: + G30 + B31 + G32 + B33 + + + + + + + + + diff --git a/Documentation/DocBook/v4l/pixfmt-y10.xml b/Documentation/DocBook/v4l/pixfmt-y10.xml new file mode 100644 index 000000000000..d065043db8d8 --- /dev/null +++ b/Documentation/DocBook/v4l/pixfmt-y10.xml @@ -0,0 +1,79 @@ + + + V4L2_PIX_FMT_Y10 ('Y10 ') + &manvol; + + + V4L2_PIX_FMT_Y10 + Grey-scale image + + + Description + + This is a grey-scale image with a depth of 10 bits per pixel. Pixels +are stored in 16-bit words with unused high bits padded with 0. The least +significant byte is stored at lower memory addresses (little-endian). + + + <constant>V4L2_PIX_FMT_Y10</constant> 4 × 4 +pixel image + + + Byte Order. + Each cell is one byte. + + + + + + start + 0: + Y'00low + Y'00high + Y'01low + Y'01high + Y'02low + Y'02high + Y'03low + Y'03high + + + start + 8: + Y'10low + Y'10high + Y'11low + Y'11high + Y'12low + Y'12high + Y'13low + Y'13high + + + start + 16: + Y'20low + Y'20high + Y'21low + Y'21high + Y'22low + Y'22high + Y'23low + Y'23high + + + start + 24: + Y'30low + Y'30high + Y'31low + Y'31high + Y'32low + Y'32high + Y'33low + Y'33high + + + + + + + + + diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml index 861664dfd6f0..d7c467187095 100644 --- a/Documentation/DocBook/v4l/pixfmt.xml +++ b/Documentation/DocBook/v4l/pixfmt.xml @@ -566,7 +566,9 @@ access the palette, this must be done with ioctls of the Linux framebuffer API.< &sub-sbggr8; &sub-sgbrg8; &sub-sgrbg8; + &sub-srggb8; &sub-sbggr16; + &sub-srggb10;
@@ -589,6 +591,7 @@ information. &sub-packed-yuv; &sub-grey; + &sub-y10; &sub-y16; &sub-yuyv; &sub-uyvy; @@ -710,11 +713,6 @@ kernel sources in the file Documentation/video4linux/cx2341x/README.hm 'S561' Compressed GBRG Bayer format used by the gspca driver. - - V4L2_PIX_FMT_SGRBG10 - 'DA10' - 10 bit raw Bayer, expanded to 16 bits. - V4L2_PIX_FMT_SGRBG10DPCM8 'DB10' -- cgit v1.2.3-70-g09d2 From d8d627834b1f4dd21a63c2b524e9eb56173df57b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 23 Oct 2010 09:28:12 -0200 Subject: videodev2.h.xml: Update to reflect the latest changes at videodev2.h Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/videodev2.h.xml | 104 +++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/videodev2.h.xml b/Documentation/DocBook/v4l/videodev2.h.xml index eda385891ade..325b23b6964c 100644 --- a/Documentation/DocBook/v4l/videodev2.h.xml +++ b/Documentation/DocBook/v4l/videodev2.h.xml @@ -154,23 +154,13 @@ enum v4l2_buf_type { V4L2_BUF_TYPE_VBI_OUTPUT = 5, V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6, V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7, -#if 1 /*KEEP*/ +#if 1 /* Experimental */ V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8, #endif V4L2_BUF_TYPE_PRIVATE = 0x80, }; -enum v4l2_ctrl_type { - V4L2_CTRL_TYPE_INTEGER = 1, - V4L2_CTRL_TYPE_BOOLEAN = 2, - V4L2_CTRL_TYPE_MENU = 3, - V4L2_CTRL_TYPE_BUTTON = 4, - V4L2_CTRL_TYPE_INTEGER64 = 5, - V4L2_CTRL_TYPE_CTRL_CLASS = 6, - V4L2_CTRL_TYPE_STRING = 7, -}; - enum v4l2_tuner_type { V4L2_TUNER_RADIO = 1, V4L2_TUNER_ANALOG_TV = 2, @@ -288,6 +278,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_RGB565 v4l2_fourcc('R', 'G', 'B', 'P') /* 16 RGB-5-6-5 */ #define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16 RGB-5-5-5 BE */ #define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16 RGB-5-6-5 BE */ +#define V4L2_PIX_FMT_BGR666 v4l2_fourcc('B', 'G', 'R', 'H') /* 18 BGR-6-6-6 */ #define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B', 'G', 'R', '3') /* 24 BGR-8-8-8 */ #define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R', 'G', 'B', '3') /* 24 RGB-8-8-8 */ #define V4L2_PIX_FMT_BGR32 v4l2_fourcc('B', 'G', 'R', '4') /* 32 BGR-8-8-8-8 */ @@ -295,6 +286,9 @@ struct v4l2_pix_format { /* Grey formats */ #define V4L2_PIX_FMT_GREY v4l2_fourcc('G', 'R', 'E', 'Y') /* 8 Greyscale */ +#define V4L2_PIX_FMT_Y4 v4l2_fourcc('Y', '0', '4', ' ') /* 4 Greyscale */ +#define V4L2_PIX_FMT_Y6 v4l2_fourcc('Y', '0', '6', ' ') /* 6 Greyscale */ +#define V4L2_PIX_FMT_Y10 v4l2_fourcc('Y', '1', '0', ' ') /* 10 Greyscale */ #define V4L2_PIX_FMT_Y16 v4l2_fourcc('Y', '1', '6', ' ') /* 16 Greyscale */ /* Palette formats */ @@ -330,7 +324,11 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */ #define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */ #define V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G') /* 8 GRGR.. BGBG.. */ -#define V4L2_PIX_FMT_SGRBG10 v4l2_fourcc('B', 'A', '1', '0') /* 10bit raw bayer */ +#define V4L2_PIX_FMT_SRGGB8 v4l2_fourcc('R', 'G', 'G', 'B') /* 8 RGRG.. GBGB.. */ +#define V4L2_PIX_FMT_SBGGR10 v4l2_fourcc('B', 'G', '1', '0') /* 10 BGBG.. GRGR.. */ +#define V4L2_PIX_FMT_SGBRG10 v4l2_fourcc('G', 'B', '1', '0') /* 10 GBGB.. RGRG.. */ +#define V4L2_PIX_FMT_SGRBG10 v4l2_fourcc('B', 'A', '1', '0') /* 10 GRGR.. BGBG.. */ +#define V4L2_PIX_FMT_SRGGB10 v4l2_fourcc('R', 'G', '1', '0') /* 10 RGRG.. GBGB.. */ /* 10bit raw bayer DPCM compressed to 8 bits */ #define V4L2_PIX_FMT_SGRBG10DPCM8 v4l2_fourcc('B', 'D', '1', '0') /* @@ -346,6 +344,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_MPEG v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4 */ /* Vendor-specific formats */ +#define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */ #define V4L2_PIX_FMT_WNVA v4l2_fourcc('W', 'N', 'V', 'A') /* Winnov hw compress */ #define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S', '9', '1', '0') /* SN9C10x compression */ #define V4L2_PIX_FMT_SN9C20X_I420 v4l2_fourcc('S', '9', '2', '0') /* SN9C20x YUV 4:2:0 */ @@ -358,12 +357,13 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */ #define V4L2_PIX_FMT_MR97310A v4l2_fourcc('M', '3', '1', '0') /* compressed BGGR bayer */ +#define V4L2_PIX_FMT_SN9C2028 v4l2_fourcc('S', 'O', 'N', 'X') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_SQ905C v4l2_fourcc('9', '0', '5', 'C') /* compressed RGGB bayer */ #define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */ #define V4L2_PIX_FMT_OV511 v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */ #define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */ -#define V4L2_PIX_FMT_TM6000 v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */ #define V4L2_PIX_FMT_STV0680 v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */ +#define V4L2_PIX_FMT_TM6000 v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */ #define V4L2_PIX_FMT_CIT_YYVYUY v4l2_fourcc('C', 'I', 'T', 'V') /* one line of Y then 1 line of VYUY */ #define V4L2_PIX_FMT_KONICA420 v4l2_fourcc('K', 'O', 'N', 'I') /* YUV420 planar in blocks of 256 pixels */ @@ -382,7 +382,7 @@ struct v4l2_fmtdesc { #define V4L2_FMT_FLAG_COMPRESSED 0x0001 #define V4L2_FMT_FLAG_EMULATED 0x0002 -#if 1 /*KEEP*/ +#if 1 /* Experimental Frame Size and frame rate enumeration */ /* * F R A M E S I Z E E N U M E R A T I O N @@ -546,6 +546,8 @@ struct v4l2_buffer { #define V4L2_BUF_FLAG_KEYFRAME 0x0008 /* Image is a keyframe (I-frame) */ #define V4L2_BUF_FLAG_PFRAME 0x0010 /* Image is a P-frame */ #define V4L2_BUF_FLAG_BFRAME 0x0020 /* Image is a B-frame */ +/* Buffer is ready, but the data contained within is corrupted. */ +#define V4L2_BUF_FLAG_ERROR 0x0040 #define V4L2_BUF_FLAG_TIMECODE 0x0100 /* timecode field is valid */ #define V4L2_BUF_FLAG_INPUT 0x0200 /* input field is valid */ @@ -936,6 +938,16 @@ struct v4l2_ext_controls { #define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL) #define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000) +enum v4l2_ctrl_type { + V4L2_CTRL_TYPE_INTEGER = 1, + V4L2_CTRL_TYPE_BOOLEAN = 2, + V4L2_CTRL_TYPE_MENU = 3, + V4L2_CTRL_TYPE_BUTTON = 4, + V4L2_CTRL_TYPE_INTEGER64 = 5, + V4L2_CTRL_TYPE_CTRL_CLASS = 6, + V4L2_CTRL_TYPE_STRING = 7, +}; + /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ struct v4l2_queryctrl { __u32 id; @@ -1020,21 +1032,27 @@ enum v4l2_colorfx { V4L2_COLORFX_NONE = 0, V4L2_COLORFX_BW = 1, V4L2_COLORFX_SEPIA = 2, - V4L2_COLORFX_NEGATIVE = 3, - V4L2_COLORFX_EMBOSS = 4, - V4L2_COLORFX_SKETCH = 5, - V4L2_COLORFX_SKY_BLUE = 6, + V4L2_COLORFX_NEGATIVE = 3, + V4L2_COLORFX_EMBOSS = 4, + V4L2_COLORFX_SKETCH = 5, + V4L2_COLORFX_SKY_BLUE = 6, V4L2_COLORFX_GRASS_GREEN = 7, V4L2_COLORFX_SKIN_WHITEN = 8, - V4L2_COLORFX_VIVID = 9. + V4L2_COLORFX_VIVID = 9, }; #define V4L2_CID_AUTOBRIGHTNESS (V4L2_CID_BASE+32) #define V4L2_CID_BAND_STOP_FILTER (V4L2_CID_BASE+33) #define V4L2_CID_ROTATE (V4L2_CID_BASE+34) #define V4L2_CID_BG_COLOR (V4L2_CID_BASE+35) + +#define V4L2_CID_CHROMA_GAIN (V4L2_CID_BASE+36) + +#define V4L2_CID_ILLUMINATORS_1 (V4L2_CID_BASE+37) +#define V4L2_CID_ILLUMINATORS_2 (V4L2_CID_BASE+38) + /* last CID + 1 */ -#define V4L2_CID_LASTP1 (V4L2_CID_BASE+36) +#define V4L2_CID_LASTP1 (V4L2_CID_BASE+39) /* MPEG-class control IDs defined by V4L2 */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) @@ -1351,6 +1369,8 @@ struct v4l2_modulator { #define V4L2_TUNER_CAP_SAP 0x0020 #define V4L2_TUNER_CAP_LANG1 0x0040 #define V4L2_TUNER_CAP_RDS 0x0080 +#define V4L2_TUNER_CAP_RDS_BLOCK_IO 0x0100 +#define V4L2_TUNER_CAP_RDS_CONTROLS 0x0200 /* Flags for the 'rxsubchans' field */ #define V4L2_TUNER_SUB_MONO 0x0001 @@ -1380,7 +1400,8 @@ struct v4l2_hw_freq_seek { enum v4l2_tuner_type type; __u32 seek_upward; __u32 wrap_around; - __u32 reserved[8]; + __u32 spacing; + __u32 reserved[7]; }; /* @@ -1435,7 +1456,7 @@ struct v4l2_audioout { * * NOTE: EXPERIMENTAL API */ -#if 1 /*KEEP*/ +#if 1 #define V4L2_ENC_IDX_FRAME_I (0) #define V4L2_ENC_IDX_FRAME_P (1) #define V4L2_ENC_IDX_FRAME_B (2) @@ -1627,6 +1648,38 @@ struct v4l2_streamparm { } parm; }; +/* + * E V E N T S + */ + +#define V4L2_EVENT_ALL 0 +#define V4L2_EVENT_VSYNC 1 +#define V4L2_EVENT_EOS 2 +#define V4L2_EVENT_PRIVATE_START 0x08000000 + +/* Payload for V4L2_EVENT_VSYNC */ +struct v4l2_event_vsync { + /* Can be V4L2_FIELD_ANY, _NONE, _TOP or _BOTTOM */ + __u8 field; +} __attribute__ ((packed)); + +struct v4l2_event { + __u32 type; + union { + struct v4l2_event_vsync vsync; + __u8 data[64]; + } u; + __u32 pending; + __u32 sequence; + struct timespec timestamp; + __u32 reserved[9]; +}; + +struct v4l2_event_subscription { + __u32 type; + __u32 reserved[7]; +}; + /* * A D V A N C E D D E B U G G I N G * @@ -1722,7 +1775,7 @@ struct v4l2_dbg_chip_ident { #define VIDIOC_G_EXT_CTRLS _IOWR('V', 71, struct v4l2_ext_controls) #define VIDIOC_S_EXT_CTRLS _IOWR('V', 72, struct v4l2_ext_controls) #define VIDIOC_TRY_EXT_CTRLS _IOWR('V', 73, struct v4l2_ext_controls) -#if 1 /*KEEP*/ +#if 1 #define VIDIOC_ENUM_FRAMESIZES _IOWR('V', 74, struct v4l2_frmsizeenum) #define VIDIOC_ENUM_FRAMEINTERVALS _IOWR('V', 75, struct v4l2_frmivalenum) #define VIDIOC_G_ENC_INDEX _IOR('V', 76, struct v4l2_enc_idx) @@ -1730,7 +1783,7 @@ struct v4l2_dbg_chip_ident { #define VIDIOC_TRY_ENCODER_CMD _IOWR('V', 78, struct v4l2_encoder_cmd) #endif -#if 1 /*KEEP*/ +#if 1 /* Experimental, meant for debugging, testing and internal use. Only implemented if CONFIG_VIDEO_ADV_DEBUG is defined. You must be root to use these ioctls. Never use these in applications! */ @@ -1749,6 +1802,9 @@ struct v4l2_dbg_chip_ident { #define VIDIOC_QUERY_DV_PRESET _IOR('V', 86, struct v4l2_dv_preset) #define VIDIOC_S_DV_TIMINGS _IOWR('V', 87, struct v4l2_dv_timings) #define VIDIOC_G_DV_TIMINGS _IOWR('V', 88, struct v4l2_dv_timings) +#define VIDIOC_DQEVENT _IOR('V', 89, struct v4l2_event) +#define VIDIOC_SUBSCRIBE_EVENT _IOW('V', 90, struct v4l2_event_subscription) +#define VIDIOC_UNSUBSCRIBE_EVENT _IOW('V', 91, struct v4l2_event_subscription) /* Reminder: when adding new ioctls please add support for them to drivers/media/video/v4l2-compat-ioctl32.c as well! */ -- cgit v1.2.3-70-g09d2 From 6c2754c28f2388a276fe21edde826f2113c8f60e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 23 Oct 2010 08:14:12 -0700 Subject: Revert "tty: Add a new file /proc/tty/consoles" This reverts commit f4a3e0bceb57466c31757f25e4e0ed108d1299ec. Jiri Sladby points out that the tty structure we're using may already be gone, and Al Viro doesn't hold back in complaining about the random loading of 'filp->private_data' which doesn't have to be a pointer at all, nor does checking the magic field for TTY_MAGIC prove anything. Belated review by Al: "a) global variable depending on stdin of the last opener? Affecting output of read(2)? Really? b) iterator is broken; list should be locked in ->start(), unlocked in ->stop() and *NOT* unlocked/relocked in ->next() c) ->show() ought to do nothing in case of ->device == NULL, instead of skipping those in ->next()/->start() d) regardless of the merits of the bright idea about asterisk at that line in output *and* regardless of (a), the implementation is not only atrociously ugly, it's actually very likely to be a roothole. Verifying that Cthulhu knows what number happens to be address of a tty_struct by blindly dereferencing memory at that address... Ouch. Please revert that crap." And Christoph pipes in and NAK's the approach of walking fd tables etc too. So it's pretty unanimous. Noticed-by: Jri Slaby Requested-by: Al Viro Cc: Greg Kroah-Hartman Cc: Werner Fink Cc: Alan Cox Cc: Christoph Hellwig Signed-off-by: Linus Torvalds --- Documentation/filesystems/proc.txt | 32 -------- fs/proc/proc_tty.c | 158 ------------------------------------- 2 files changed, 190 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 98223a676940..a6aca8740883 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -1075,7 +1075,6 @@ Table 1-11: Files in /proc/tty drivers list of drivers and their usage ldiscs registered line disciplines driver/serial usage statistic and status of single tty lines - consoles registered system console lines .............................................................................. To see which tty's are currently in use, you can simply look into the file @@ -1094,37 +1093,6 @@ To see which tty's are currently in use, you can simply look into the file /dev/tty /dev/tty 5 0 system:/dev/tty unknown /dev/tty 4 1-63 console -To see which character device lines are currently used for the system console -/dev/console, you may simply look into the file /proc/tty/consoles: - - > cat /proc/tty/consoles - tty0 -WU (ECp) 4:7 - ttyS0 -W- (Ep) 4:64 - -The columns are: - - device name of the device - operations R = can do read operations - W = can do write operations - U = can do unblank - flags E = it is enabled - C = it is prefered console - B = it is primary boot console - p = it is used for printk buffer - b = it is not a TTY but a Braille device - a = it is safe to use when cpu is offline - * = it is standard input of the reading process - major:minor major and minor number of the device separated by a colon - -If the reading process holds /dev/console open at the regular standard input -stream the active device will be marked by an asterisk: - - > cat /proc/tty/consoles < /dev/console - tty0 -WU (ECp*) 4:7 - ttyS0 -W- (Ep) 4:64 - > tty - /dev/pts/3 - 1.8 Miscellaneous kernel statistics in /proc/stat ------------------------------------------------- diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c index dc44f94022f1..83adcc869437 100644 --- a/fs/proc/proc_tty.c +++ b/fs/proc/proc_tty.c @@ -12,10 +12,7 @@ #include #include #include -#include -#include #include -#include #include /* @@ -139,160 +136,6 @@ static const struct file_operations proc_tty_drivers_operations = { .release = seq_release, }; -/* - * The device ID of file descriptor 0 of the current reading - * task if a character device... - */ -static dev_t current_dev; - -/* - * This is the handler for /proc/tty/consoles - */ -static int show_console_dev(struct seq_file *m, void *v) -{ - const struct tty_driver *driver; - struct console *con; - int index, len; - char flags[10]; - dev_t dev; - - if (v == SEQ_START_TOKEN) - return 0; - con = (struct console *)v; - if (!con) - return 0; - driver = con->device(con, &index); - if (!driver) - return 0; - dev = MKDEV(driver->major, driver->minor_start) + index; - - index = 0; - if (con->flags & CON_ENABLED) - flags[index++] = 'E'; - if (con->flags & CON_CONSDEV) - flags[index++] = 'C'; - if (con->flags & CON_BOOT) - flags[index++] = 'B'; - if (con->flags & CON_PRINTBUFFER) - flags[index++] = 'p'; - if (con->flags & CON_BRL) - flags[index++] = 'b'; - if (con->flags & CON_ANYTIME) - flags[index++] = 'a'; - if (current_dev == dev) - flags[index++] = '*'; - flags[index] = 0; - - seq_printf(m, "%s%d%n", con->name, con->index, &len); - len = 21 - len; - if (len < 1) - len = 1; - seq_printf(m, "%*c", len, ' '); - seq_printf(m, "%c%c%c (%s)%n", con->read ? 'R' : '-', - con->write ? 'W' : '-', con->unblank ? 'U' : '-', - flags, &len); - len = 13 - len; - if (len < 1) - len = 1; - seq_printf(m, "%*c%4d:%d\n", len, ' ', MAJOR(dev), MINOR(dev)); - - return 0; -} - -/* iterator for consoles */ -static void *c_start(struct seq_file *m, loff_t *pos) -{ - struct console *con; - loff_t off = 0; - - if (*pos == 0) - return SEQ_START_TOKEN; - - acquire_console_sem(); - for (con = console_drivers; con; con = con->next) { - if (!con->device) - continue; - if (++off == *pos) - break; - } - release_console_sem(); - - return con; -} - -static void *c_next(struct seq_file *m, void *v, loff_t *pos) -{ - struct console *con; - - acquire_console_sem(); - if (v == SEQ_START_TOKEN) - con = console_drivers; - else - con = ((struct console *)v)->next; - for (; con; con = con->next) { - if (!con->device) - continue; - ++*pos; - break; - } - release_console_sem(); - - return con; -} - -static void c_stop(struct seq_file *m, void *v) -{ -} - -static const struct seq_operations tty_consoles_op = { - .start = c_start, - .next = c_next, - .stop = c_stop, - .show = show_console_dev -}; - -/* - * Used for open /proc/tty/consoles. Before this detect - * the device ID of file descriptor 0 of the current - * reading task if a character device... - */ -static int tty_consoles_open(struct inode *inode, struct file *file) -{ - struct files_struct *curfiles; - - current_dev = 0; - curfiles = get_files_struct(current); - if (curfiles) { - const struct file *curfp; - spin_lock(&curfiles->file_lock); - curfp = fcheck_files(curfiles, 0); - if (curfp && curfp->private_data) { - const struct inode *inode; - dget(curfp->f_dentry); - inode = curfp->f_dentry->d_inode; - if (S_ISCHR(inode->i_mode)) { - struct tty_struct *tty; - tty = (struct tty_struct *)curfp->private_data; - if (tty && tty->magic == TTY_MAGIC) { - tty = tty_pair_get_tty(tty); - current_dev = tty_devnum(tty); - } - } - dput(curfp->f_dentry); - } - spin_unlock(&curfiles->file_lock); - put_files_struct(curfiles); - } - return seq_open(file, &tty_consoles_op); -} - -static const struct file_operations proc_tty_consoles_operations = { - .open = tty_consoles_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - /* * This function is called by tty_register_driver() to handle * registering the driver's /proc handler into /proc/tty/driver/ @@ -343,5 +186,4 @@ void __init proc_tty_init(void) proc_tty_driver = proc_mkdir_mode("tty/driver", S_IRUSR|S_IXUSR, NULL); proc_create("tty/ldiscs", 0, NULL, &tty_ldiscs_proc_fops); proc_create("tty/drivers", 0, NULL, &proc_tty_drivers_operations); - proc_create("tty/consoles", 0, NULL, &proc_tty_consoles_operations); } -- cgit v1.2.3-70-g09d2 From d7d3c2ea99c4845611997cf728af88c4c232e908 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 29 Jul 2010 14:48:07 +0200 Subject: KVM: PPC: Add Documentation about PV interface We just introduced a new PV interface that screams for documentation. So here it is - a shiny new and awesome text file describing the internal works of the PPC KVM paravirtual interface. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- Documentation/kvm/ppc-pv.txt | 179 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 Documentation/kvm/ppc-pv.txt (limited to 'Documentation') diff --git a/Documentation/kvm/ppc-pv.txt b/Documentation/kvm/ppc-pv.txt new file mode 100644 index 000000000000..41ee16d954d7 --- /dev/null +++ b/Documentation/kvm/ppc-pv.txt @@ -0,0 +1,179 @@ +The PPC KVM paravirtual interface +================================= + +The basic execution principle by which KVM on PowerPC works is to run all kernel +space code in PR=1 which is user space. This way we trap all privileged +instructions and can emulate them accordingly. + +Unfortunately that is also the downfall. There are quite some privileged +instructions that needlessly return us to the hypervisor even though they +could be handled differently. + +This is what the PPC PV interface helps with. It takes privileged instructions +and transforms them into unprivileged ones with some help from the hypervisor. +This cuts down virtualization costs by about 50% on some of my benchmarks. + +The code for that interface can be found in arch/powerpc/kernel/kvm* + +Querying for existence +====================== + +To find out if we're running on KVM or not, we leverage the device tree. When +Linux is running on KVM, a node /hypervisor exists. That node contains a +compatible property with the value "linux,kvm". + +Once you determined you're running under a PV capable KVM, you can now use +hypercalls as described below. + +KVM hypercalls +============== + +Inside the device tree's /hypervisor node there's a property called +'hypercall-instructions'. This property contains at most 4 opcodes that make +up the hypercall. To call a hypercall, just call these instructions. + +The parameters are as follows: + + Register IN OUT + + r0 - volatile + r3 1st parameter Return code + r4 2nd parameter 1st output value + r5 3rd parameter 2nd output value + r6 4th parameter 3rd output value + r7 5th parameter 4th output value + r8 6th parameter 5th output value + r9 7th parameter 6th output value + r10 8th parameter 7th output value + r11 hypercall number 8th output value + r12 - volatile + +Hypercall definitions are shared in generic code, so the same hypercall numbers +apply for x86 and powerpc alike with the exception that each KVM hypercall +also needs to be ORed with the KVM vendor code which is (42 << 16). + +Return codes can be as follows: + + Code Meaning + + 0 Success + 12 Hypercall not implemented + <0 Error + +The magic page +============== + +To enable communication between the hypervisor and guest there is a new shared +page that contains parts of supervisor visible register state. The guest can +map this shared page using the KVM hypercall KVM_HC_PPC_MAP_MAGIC_PAGE. + +With this hypercall issued the guest always gets the magic page mapped at the +desired location in effective and physical address space. For now, we always +map the page to -4096. This way we can access it using absolute load and store +functions. The following instruction reads the first field of the magic page: + + ld rX, -4096(0) + +The interface is designed to be extensible should there be need later to add +additional registers to the magic page. If you add fields to the magic page, +also define a new hypercall feature to indicate that the host can give you more +registers. Only if the host supports the additional features, make use of them. + +The magic page has the following layout as described in +arch/powerpc/include/asm/kvm_para.h: + +struct kvm_vcpu_arch_shared { + __u64 scratch1; + __u64 scratch2; + __u64 scratch3; + __u64 critical; /* Guest may not get interrupts if == r1 */ + __u64 sprg0; + __u64 sprg1; + __u64 sprg2; + __u64 sprg3; + __u64 srr0; + __u64 srr1; + __u64 dar; + __u64 msr; + __u32 dsisr; + __u32 int_pending; /* Tells the guest if we have an interrupt */ +}; + +Additions to the page must only occur at the end. Struct fields are always 32 +or 64 bit aligned, depending on them being 32 or 64 bit wide respectively. + +MSR bits +======== + +The MSR contains bits that require hypervisor intervention and bits that do +not require direct hypervisor intervention because they only get interpreted +when entering the guest or don't have any impact on the hypervisor's behavior. + +The following bits are safe to be set inside the guest: + + MSR_EE + MSR_RI + MSR_CR + MSR_ME + +If any other bit changes in the MSR, please still use mtmsr(d). + +Patched instructions +==================== + +The "ld" and "std" instructions are transormed to "lwz" and "stw" instructions +respectively on 32 bit systems with an added offset of 4 to accomodate for big +endianness. + +The following is a list of mapping the Linux kernel performs when running as +guest. Implementing any of those mappings is optional, as the instruction traps +also act on the shared page. So calling privileged instructions still works as +before. + +From To +==== == + +mfmsr rX ld rX, magic_page->msr +mfsprg rX, 0 ld rX, magic_page->sprg0 +mfsprg rX, 1 ld rX, magic_page->sprg1 +mfsprg rX, 2 ld rX, magic_page->sprg2 +mfsprg rX, 3 ld rX, magic_page->sprg3 +mfsrr0 rX ld rX, magic_page->srr0 +mfsrr1 rX ld rX, magic_page->srr1 +mfdar rX ld rX, magic_page->dar +mfdsisr rX lwz rX, magic_page->dsisr + +mtmsr rX std rX, magic_page->msr +mtsprg 0, rX std rX, magic_page->sprg0 +mtsprg 1, rX std rX, magic_page->sprg1 +mtsprg 2, rX std rX, magic_page->sprg2 +mtsprg 3, rX std rX, magic_page->sprg3 +mtsrr0 rX std rX, magic_page->srr0 +mtsrr1 rX std rX, magic_page->srr1 +mtdar rX std rX, magic_page->dar +mtdsisr rX stw rX, magic_page->dsisr + +tlbsync nop + +mtmsrd rX, 0 b +mtmsr rX b + +mtmsrd rX, 1 b + +[BookE only] +wrteei [0|1] b + + +Some instructions require more logic to determine what's going on than a load +or store instruction can deliver. To enable patching of those, we keep some +RAM around where we can live translate instructions to. What happens is the +following: + + 1) copy emulation code to memory + 2) patch that code to fit the emulated instruction + 3) patch that code to return to the original pc + 4 + 4) patch the original instruction to branch to the new code + +That way we can inject an arbitrary amount of code as replacement for a single +instruction. This allows us to check for pending interrupts when setting EE=1 +for example. -- cgit v1.2.3-70-g09d2 From 15711e9c927bfc08e66791cbf0ca7887c0880768 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 29 Jul 2010 14:48:08 +0200 Subject: KVM: PPC: Add get_pvinfo interface to query hypercall instructions We need to tell the guest the opcodes that make up a hypercall through interfaces that are controlled by userspace. So we need to add a call for userspace to allow it to query those opcodes so it can pass them on. This is required because the hypercall opcodes can change based on the hypervisor conditions. If we're running in hardware accelerated hypervisor mode, a hypercall looks different from when we're running without hardware acceleration. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- Documentation/kvm/api.txt | 23 +++++++++++++++++++++++ arch/powerpc/kvm/powerpc.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/kvm.h | 11 +++++++++++ 3 files changed, 72 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kvm/api.txt b/Documentation/kvm/api.txt index 5f5b64982b1a..44d9893f9db1 100644 --- a/Documentation/kvm/api.txt +++ b/Documentation/kvm/api.txt @@ -1032,6 +1032,29 @@ are defined as follows: eax, ebx, ecx, edx: the values returned by the cpuid instruction for this function/index combination +4.46 KVM_PPC_GET_PVINFO + +Capability: KVM_CAP_PPC_GET_PVINFO +Architectures: ppc +Type: vm ioctl +Parameters: struct kvm_ppc_pvinfo (out) +Returns: 0 on success, !0 on error + +struct kvm_ppc_pvinfo { + __u32 flags; + __u32 hcall[4]; + __u8 pad[108]; +}; + +This ioctl fetches PV specific information that need to be passed to the guest +using the device tree or other means from vm context. + +For now the only implemented piece of information distributed here is an array +of 4 instructions that make up a hypercall. + +If any additional field gets added to this structure later on, a bit for that +additional piece of information will be set in the flags bitmap. + 5. The kvm_run structure Application code obtains a pointer to the kvm_run structure by diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index fecfe043458d..6a53a3f86dae 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -191,6 +191,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_PPC_UNSET_IRQ: case KVM_CAP_ENABLE_CAP: case KVM_CAP_PPC_OSI: + case KVM_CAP_PPC_GET_PVINFO: r = 1; break; case KVM_CAP_COALESCED_MMIO: @@ -578,16 +579,53 @@ out: return r; } +static int kvm_vm_ioctl_get_pvinfo(struct kvm_ppc_pvinfo *pvinfo) +{ + u32 inst_lis = 0x3c000000; + u32 inst_ori = 0x60000000; + u32 inst_nop = 0x60000000; + u32 inst_sc = 0x44000002; + u32 inst_imm_mask = 0xffff; + + /* + * The hypercall to get into KVM from within guest context is as + * follows: + * + * lis r0, r0, KVM_SC_MAGIC_R0@h + * ori r0, KVM_SC_MAGIC_R0@l + * sc + * nop + */ + pvinfo->hcall[0] = inst_lis | ((KVM_SC_MAGIC_R0 >> 16) & inst_imm_mask); + pvinfo->hcall[1] = inst_ori | (KVM_SC_MAGIC_R0 & inst_imm_mask); + pvinfo->hcall[2] = inst_sc; + pvinfo->hcall[3] = inst_nop; + + return 0; +} + long kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { + void __user *argp = (void __user *)arg; long r; switch (ioctl) { + case KVM_PPC_GET_PVINFO: { + struct kvm_ppc_pvinfo pvinfo; + r = kvm_vm_ioctl_get_pvinfo(&pvinfo); + if (copy_to_user(argp, &pvinfo, sizeof(pvinfo))) { + r = -EFAULT; + goto out; + } + + break; + } default: r = -ENOTTY; } +out: return r; } diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 636fc381c897..37077045970b 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -414,6 +414,14 @@ struct kvm_enable_cap { __u8 pad[64]; }; +/* for KVM_PPC_GET_PVINFO */ +struct kvm_ppc_pvinfo { + /* out */ + __u32 flags; + __u32 hcall[4]; + __u8 pad[108]; +}; + #define KVMIO 0xAE /* @@ -530,6 +538,7 @@ struct kvm_enable_cap { #ifdef __KVM_HAVE_XCRS #define KVM_CAP_XCRS 56 #endif +#define KVM_CAP_PPC_GET_PVINFO 57 #ifdef KVM_CAP_IRQ_ROUTING @@ -664,6 +673,8 @@ struct kvm_clock_data { /* Available with KVM_CAP_PIT_STATE2 */ #define KVM_GET_PIT2 _IOR(KVMIO, 0x9f, struct kvm_pit_state2) #define KVM_SET_PIT2 _IOW(KVMIO, 0xa0, struct kvm_pit_state2) +/* Available with KVM_CAP_PPC_GET_PVINFO */ +#define KVM_PPC_GET_PVINFO _IOW(KVMIO, 0xa1, struct kvm_ppc_pvinfo) /* * ioctls for vcpu fds -- cgit v1.2.3-70-g09d2 From 9cf4c4fcb59704f0c50f6aa7933399db5d2edf1a Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 16 Aug 2010 17:51:20 +0200 Subject: KVM: x86: explain 'no-kvmclock' kernel parameter no-kvmclock kernel parameter is missing its explanation in Documentation/kernel-parameters.txt. Add it. Signed-off-by: Jiri Kosina Signed-off-by: Avi Kivity --- Documentation/kernel-parameters.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 8dd7248508a9..6bd23751a2ce 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1693,6 +1693,8 @@ and is between 256 and 4096 characters. It is defined in the file nojitter [IA64] Disables jitter checking for ITC timers. + no-kvmclock [X86,KVM] Disable paravirtualized KVM clock driver + nolapic [X86-32,APIC] Do not enable or use the local APIC. nolapic_timer [X86-32,APIC] Do not use the local APIC timer. -- cgit v1.2.3-70-g09d2 From f392eb2546170e539668a5ab8df6c1254d15a201 Mon Sep 17 00:00:00 2001 From: Zachary Amsden Date: Thu, 19 Aug 2010 22:07:33 -1000 Subject: KVM: x86: Add timekeeping documentation Basic informational document about x86 timekeeping and how KVM is affected. Signed-off-by: Zachary Amsden Signed-off-by: Marcelo Tosatti --- Documentation/kvm/timekeeping.txt | 612 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 612 insertions(+) create mode 100644 Documentation/kvm/timekeeping.txt (limited to 'Documentation') diff --git a/Documentation/kvm/timekeeping.txt b/Documentation/kvm/timekeeping.txt new file mode 100644 index 000000000000..0c5033a58c9e --- /dev/null +++ b/Documentation/kvm/timekeeping.txt @@ -0,0 +1,612 @@ + + Timekeeping Virtualization for X86-Based Architectures + + Zachary Amsden + Copyright (c) 2010, Red Hat. All rights reserved. + +1) Overview +2) Timing Devices +3) TSC Hardware +4) Virtualization Problems + +========================================================================= + +1) Overview + +One of the most complicated parts of the X86 platform, and specifically, +the virtualization of this platform is the plethora of timing devices available +and the complexity of emulating those devices. In addition, virtualization of +time introduces a new set of challenges because it introduces a multiplexed +division of time beyond the control of the guest CPU. + +First, we will describe the various timekeeping hardware available, then +present some of the problems which arise and solutions available, giving +specific recommendations for certain classes of KVM guests. + +The purpose of this document is to collect data and information relevant to +timekeeping which may be difficult to find elsewhere, specifically, +information relevant to KVM and hardware-based virtualization. + +========================================================================= + +2) Timing Devices + +First we discuss the basic hardware devices available. TSC and the related +KVM clock are special enough to warrant a full exposition and are described in +the following section. + +2.1) i8254 - PIT + +One of the first timer devices available is the programmable interrupt timer, +or PIT. The PIT has a fixed frequency 1.193182 MHz base clock and three +channels which can be programmed to deliver periodic or one-shot interrupts. +These three channels can be configured in different modes and have individual +counters. Channel 1 and 2 were not available for general use in the original +IBM PC, and historically were connected to control RAM refresh and the PC +speaker. Now the PIT is typically integrated as part of an emulated chipset +and a separate physical PIT is not used. + +The PIT uses I/O ports 0x40 - 0x43. Access to the 16-bit counters is done +using single or multiple byte access to the I/O ports. There are 6 modes +available, but not all modes are available to all timers, as only timer 2 +has a connected gate input, required for modes 1 and 5. The gate line is +controlled by port 61h, bit 0, as illustrated in the following diagram. + + -------------- ---------------- +| | | | +| 1.1932 MHz |---------->| CLOCK OUT | ---------> IRQ 0 +| Clock | | | | + -------------- | +->| GATE TIMER 0 | + | ---------------- + | + | ---------------- + | | | + |------>| CLOCK OUT | ---------> 66.3 KHZ DRAM + | | | (aka /dev/null) + | +->| GATE TIMER 1 | + | ---------------- + | + | ---------------- + | | | + |------>| CLOCK OUT | ---------> Port 61h, bit 5 + | | | +Port 61h, bit 0 ---------->| GATE TIMER 2 | \_.---- ____ + ---------------- _| )--|LPF|---Speaker + / *---- \___/ +Port 61h, bit 1 -----------------------------------/ + +The timer modes are now described. + +Mode 0: Single Timeout. This is a one-shot software timeout that counts down + when the gate is high (always true for timers 0 and 1). When the count + reaches zero, the output goes high. + +Mode 1: Triggered One-shot. The output is intially set high. When the gate + line is set high, a countdown is initiated (which does not stop if the gate is + lowered), during which the output is set low. When the count reaches zero, + the output goes high. + +Mode 2: Rate Generator. The output is initially set high. When the countdown + reaches 1, the output goes low for one count and then returns high. The value + is reloaded and the countdown automatically resumes. If the gate line goes + low, the count is halted. If the output is low when the gate is lowered, the + output automatically goes high (this only affects timer 2). + +Mode 3: Square Wave. This generates a high / low square wave. The count + determines the length of the pulse, which alternates between high and low + when zero is reached. The count only proceeds when gate is high and is + automatically reloaded on reaching zero. The count is decremented twice at + each clock to generate a full high / low cycle at the full periodic rate. + If the count is even, the clock remains high for N/2 counts and low for N/2 + counts; if the clock is odd, the clock is high for (N+1)/2 counts and low + for (N-1)/2 counts. Only even values are latched by the counter, so odd + values are not observed when reading. This is the intended mode for timer 2, + which generates sine-like tones by low-pass filtering the square wave output. + +Mode 4: Software Strobe. After programming this mode and loading the counter, + the output remains high until the counter reaches zero. Then the output + goes low for 1 clock cycle and returns high. The counter is not reloaded. + Counting only occurs when gate is high. + +Mode 5: Hardware Strobe. After programming and loading the counter, the + output remains high. When the gate is raised, a countdown is initiated + (which does not stop if the gate is lowered). When the counter reaches zero, + the output goes low for 1 clock cycle and then returns high. The counter is + not reloaded. + +In addition to normal binary counting, the PIT supports BCD counting. The +command port, 0x43 is used to set the counter and mode for each of the three +timers. + +PIT commands, issued to port 0x43, using the following bit encoding: + +Bit 7-4: Command (See table below) +Bit 3-1: Mode (000 = Mode 0, 101 = Mode 5, 11X = undefined) +Bit 0 : Binary (0) / BCD (1) + +Command table: + +0000 - Latch Timer 0 count for port 0x40 + sample and hold the count to be read in port 0x40; + additional commands ignored until counter is read; + mode bits ignored. + +0001 - Set Timer 0 LSB mode for port 0x40 + set timer to read LSB only and force MSB to zero; + mode bits set timer mode + +0010 - Set Timer 0 MSB mode for port 0x40 + set timer to read MSB only and force LSB to zero; + mode bits set timer mode + +0011 - Set Timer 0 16-bit mode for port 0x40 + set timer to read / write LSB first, then MSB; + mode bits set timer mode + +0100 - Latch Timer 1 count for port 0x41 - as described above +0101 - Set Timer 1 LSB mode for port 0x41 - as described above +0110 - Set Timer 1 MSB mode for port 0x41 - as described above +0111 - Set Timer 1 16-bit mode for port 0x41 - as described above + +1000 - Latch Timer 2 count for port 0x42 - as described above +1001 - Set Timer 2 LSB mode for port 0x42 - as described above +1010 - Set Timer 2 MSB mode for port 0x42 - as described above +1011 - Set Timer 2 16-bit mode for port 0x42 as described above + +1101 - General counter latch + Latch combination of counters into corresponding ports + Bit 3 = Counter 2 + Bit 2 = Counter 1 + Bit 1 = Counter 0 + Bit 0 = Unused + +1110 - Latch timer status + Latch combination of counter mode into corresponding ports + Bit 3 = Counter 2 + Bit 2 = Counter 1 + Bit 1 = Counter 0 + + The output of ports 0x40-0x42 following this command will be: + + Bit 7 = Output pin + Bit 6 = Count loaded (0 if timer has expired) + Bit 5-4 = Read / Write mode + 01 = MSB only + 10 = LSB only + 11 = LSB / MSB (16-bit) + Bit 3-1 = Mode + Bit 0 = Binary (0) / BCD mode (1) + +2.2) RTC + +The second device which was available in the original PC was the MC146818 real +time clock. The original device is now obsolete, and usually emulated by the +system chipset, sometimes by an HPET and some frankenstein IRQ routing. + +The RTC is accessed through CMOS variables, which uses an index register to +control which bytes are read. Since there is only one index register, read +of the CMOS and read of the RTC require lock protection (in addition, it is +dangerous to allow userspace utilities such as hwclock to have direct RTC +access, as they could corrupt kernel reads and writes of CMOS memory). + +The RTC generates an interrupt which is usually routed to IRQ 8. The interrupt +can function as a periodic timer, an additional once a day alarm, and can issue +interrupts after an update of the CMOS registers by the MC146818 is complete. +The type of interrupt is signalled in the RTC status registers. + +The RTC will update the current time fields by battery power even while the +system is off. The current time fields should not be read while an update is +in progress, as indicated in the status register. + +The clock uses a 32.768kHz crystal, so bits 6-4 of register A should be +programmed to a 32kHz divider if the RTC is to count seconds. + +This is the RAM map originally used for the RTC/CMOS: + +Location Size Description +------------------------------------------ +00h byte Current second (BCD) +01h byte Seconds alarm (BCD) +02h byte Current minute (BCD) +03h byte Minutes alarm (BCD) +04h byte Current hour (BCD) +05h byte Hours alarm (BCD) +06h byte Current day of week (BCD) +07h byte Current day of month (BCD) +08h byte Current month (BCD) +09h byte Current year (BCD) +0Ah byte Register A + bit 7 = Update in progress + bit 6-4 = Divider for clock + 000 = 4.194 MHz + 001 = 1.049 MHz + 010 = 32 kHz + 10X = test modes + 110 = reset / disable + 111 = reset / disable + bit 3-0 = Rate selection for periodic interrupt + 000 = periodic timer disabled + 001 = 3.90625 uS + 010 = 7.8125 uS + 011 = .122070 mS + 100 = .244141 mS + ... + 1101 = 125 mS + 1110 = 250 mS + 1111 = 500 mS +0Bh byte Register B + bit 7 = Run (0) / Halt (1) + bit 6 = Periodic interrupt enable + bit 5 = Alarm interrupt enable + bit 4 = Update-ended interrupt enable + bit 3 = Square wave interrupt enable + bit 2 = BCD calendar (0) / Binary (1) + bit 1 = 12-hour mode (0) / 24-hour mode (1) + bit 0 = 0 (DST off) / 1 (DST enabled) +OCh byte Register C (read only) + bit 7 = interrupt request flag (IRQF) + bit 6 = periodic interrupt flag (PF) + bit 5 = alarm interrupt flag (AF) + bit 4 = update interrupt flag (UF) + bit 3-0 = reserved +ODh byte Register D (read only) + bit 7 = RTC has power + bit 6-0 = reserved +32h byte Current century BCD (*) + (*) location vendor specific and now determined from ACPI global tables + +2.3) APIC + +On Pentium and later processors, an on-board timer is available to each CPU +as part of the Advanced Programmable Interrupt Controller. The APIC is +accessed through memory-mapped registers and provides interrupt service to each +CPU, used for IPIs and local timer interrupts. + +Although in theory the APIC is a safe and stable source for local interrupts, +in practice, many bugs and glitches have occurred due to the special nature of +the APIC CPU-local memory-mapped hardware. Beware that CPU errata may affect +the use of the APIC and that workarounds may be required. In addition, some of +these workarounds pose unique constraints for virtualization - requiring either +extra overhead incurred from extra reads of memory-mapped I/O or additional +functionality that may be more computationally expensive to implement. + +Since the APIC is documented quite well in the Intel and AMD manuals, we will +avoid repetition of the detail here. It should be pointed out that the APIC +timer is programmed through the LVT (local vector timer) register, is capable +of one-shot or periodic operation, and is based on the bus clock divided down +by the programmable divider register. + +2.4) HPET + +HPET is quite complex, and was originally intended to replace the PIT / RTC +support of the X86 PC. It remains to be seen whether that will be the case, as +the de facto standard of PC hardware is to emulate these older devices. Some +systems designated as legacy free may support only the HPET as a hardware timer +device. + +The HPET spec is rather loose and vague, requiring at least 3 hardware timers, +but allowing implementation freedom to support many more. It also imposes no +fixed rate on the timer frequency, but does impose some extremal values on +frequency, error and slew. + +In general, the HPET is recommended as a high precision (compared to PIT /RTC) +time source which is independent of local variation (as there is only one HPET +in any given system). The HPET is also memory-mapped, and its presence is +indicated through ACPI tables by the BIOS. + +Detailed specification of the HPET is beyond the current scope of this +document, as it is also very well documented elsewhere. + +2.5) Offboard Timers + +Several cards, both proprietary (watchdog boards) and commonplace (e1000) have +timing chips built into the cards which may have registers which are accessible +to kernel or user drivers. To the author's knowledge, using these to generate +a clocksource for a Linux or other kernel has not yet been attempted and is in +general frowned upon as not playing by the agreed rules of the game. Such a +timer device would require additional support to be virtualized properly and is +not considered important at this time as no known operating system does this. + +========================================================================= + +3) TSC Hardware + +The TSC or time stamp counter is relatively simple in theory; it counts +instruction cycles issued by the processor, which can be used as a measure of +time. In practice, due to a number of problems, it is the most complicated +timekeeping device to use. + +The TSC is represented internally as a 64-bit MSR which can be read with the +RDMSR, RDTSC, or RDTSCP (when available) instructions. In the past, hardware +limitations made it possible to write the TSC, but generally on old hardware it +was only possible to write the low 32-bits of the 64-bit counter, and the upper +32-bits of the counter were cleared. Now, however, on Intel processors family +0Fh, for models 3, 4 and 6, and family 06h, models e and f, this restriction +has been lifted and all 64-bits are writable. On AMD systems, the ability to +write the TSC MSR is not an architectural guarantee. + +The TSC is accessible from CPL-0 and conditionally, for CPL > 0 software by +means of the CR4.TSD bit, which when enabled, disables CPL > 0 TSC access. + +Some vendors have implemented an additional instruction, RDTSCP, which returns +atomically not just the TSC, but an indicator which corresponds to the +processor number. This can be used to index into an array of TSC variables to +determine offset information in SMP systems where TSCs are not synchronized. +The presence of this instruction must be determined by consulting CPUID feature +bits. + +Both VMX and SVM provide extension fields in the virtualization hardware which +allows the guest visible TSC to be offset by a constant. Newer implementations +promise to allow the TSC to additionally be scaled, but this hardware is not +yet widely available. + +3.1) TSC synchronization + +The TSC is a CPU-local clock in most implementations. This means, on SMP +platforms, the TSCs of different CPUs may start at different times depending +on when the CPUs are powered on. Generally, CPUs on the same die will share +the same clock, however, this is not always the case. + +The BIOS may attempt to resynchronize the TSCs during the poweron process and +the operating system or other system software may attempt to do this as well. +Several hardware limitations make the problem worse - if it is not possible to +write the full 64-bits of the TSC, it may be impossible to match the TSC in +newly arriving CPUs to that of the rest of the system, resulting in +unsynchronized TSCs. This may be done by BIOS or system software, but in +practice, getting a perfectly synchronized TSC will not be possible unless all +values are read from the same clock, which generally only is possible on single +socket systems or those with special hardware support. + +3.2) TSC and CPU hotplug + +As touched on already, CPUs which arrive later than the boot time of the system +may not have a TSC value that is synchronized with the rest of the system. +Either system software, BIOS, or SMM code may actually try to establish the TSC +to a value matching the rest of the system, but a perfect match is usually not +a guarantee. This can have the effect of bringing a system from a state where +TSC is synchronized back to a state where TSC synchronization flaws, however +small, may be exposed to the OS and any virtualization environment. + +3.3) TSC and multi-socket / NUMA + +Multi-socket systems, especially large multi-socket systems are likely to have +individual clocksources rather than a single, universally distributed clock. +Since these clocks are driven by different crystals, they will not have +perfectly matched frequency, and temperature and electrical variations will +cause the CPU clocks, and thus the TSCs to drift over time. Depending on the +exact clock and bus design, the drift may or may not be fixed in absolute +error, and may accumulate over time. + +In addition, very large systems may deliberately slew the clocks of individual +cores. This technique, known as spread-spectrum clocking, reduces EMI at the +clock frequency and harmonics of it, which may be required to pass FCC +standards for telecommunications and computer equipment. + +It is recommended not to trust the TSCs to remain synchronized on NUMA or +multiple socket systems for these reasons. + +3.4) TSC and C-states + +C-states, or idling states of the processor, especially C1E and deeper sleep +states may be problematic for TSC as well. The TSC may stop advancing in such +a state, resulting in a TSC which is behind that of other CPUs when execution +is resumed. Such CPUs must be detected and flagged by the operating system +based on CPU and chipset identifications. + +The TSC in such a case may be corrected by catching it up to a known external +clocksource. + +3.5) TSC frequency change / P-states + +To make things slightly more interesting, some CPUs may change frequency. They +may or may not run the TSC at the same rate, and because the frequency change +may be staggered or slewed, at some points in time, the TSC rate may not be +known other than falling within a range of values. In this case, the TSC will +not be a stable time source, and must be calibrated against a known, stable, +external clock to be a usable source of time. + +Whether the TSC runs at a constant rate or scales with the P-state is model +dependent and must be determined by inspecting CPUID, chipset or vendor +specific MSR fields. + +In addition, some vendors have known bugs where the P-state is actually +compensated for properly during normal operation, but when the processor is +inactive, the P-state may be raised temporarily to service cache misses from +other processors. In such cases, the TSC on halted CPUs could advance faster +than that of non-halted processors. AMD Turion processors are known to have +this problem. + +3.6) TSC and STPCLK / T-states + +External signals given to the processor may also have the effect of stopping +the TSC. This is typically done for thermal emergency power control to prevent +an overheating condition, and typically, there is no way to detect that this +condition has happened. + +3.7) TSC virtualization - VMX + +VMX provides conditional trapping of RDTSC, RDMSR, WRMSR and RDTSCP +instructions, which is enough for full virtualization of TSC in any manner. In +addition, VMX allows passing through the host TSC plus an additional TSC_OFFSET +field specified in the VMCS. Special instructions must be used to read and +write the VMCS field. + +3.8) TSC virtualization - SVM + +SVM provides conditional trapping of RDTSC, RDMSR, WRMSR and RDTSCP +instructions, which is enough for full virtualization of TSC in any manner. In +addition, SVM allows passing through the host TSC plus an additional offset +field specified in the SVM control block. + +3.9) TSC feature bits in Linux + +In summary, there is no way to guarantee the TSC remains in perfect +synchronization unless it is explicitly guaranteed by the architecture. Even +if so, the TSCs in multi-sockets or NUMA systems may still run independently +despite being locally consistent. + +The following feature bits are used by Linux to signal various TSC attributes, +but they can only be taken to be meaningful for UP or single node systems. + +X86_FEATURE_TSC : The TSC is available in hardware +X86_FEATURE_RDTSCP : The RDTSCP instruction is available +X86_FEATURE_CONSTANT_TSC : The TSC rate is unchanged with P-states +X86_FEATURE_NONSTOP_TSC : The TSC does not stop in C-states +X86_FEATURE_TSC_RELIABLE : TSC sync checks are skipped (VMware) + +4) Virtualization Problems + +Timekeeping is especially problematic for virtualization because a number of +challenges arise. The most obvious problem is that time is now shared between +the host and, potentially, a number of virtual machines. Thus the virtual +operating system does not run with 100% usage of the CPU, despite the fact that +it may very well make that assumption. It may expect it to remain true to very +exacting bounds when interrupt sources are disabled, but in reality only its +virtual interrupt sources are disabled, and the machine may still be preempted +at any time. This causes problems as the passage of real time, the injection +of machine interrupts and the associated clock sources are no longer completely +synchronized with real time. + +This same problem can occur on native harware to a degree, as SMM mode may +steal cycles from the naturally on X86 systems when SMM mode is used by the +BIOS, but not in such an extreme fashion. However, the fact that SMM mode may +cause similar problems to virtualization makes it a good justification for +solving many of these problems on bare metal. + +4.1) Interrupt clocking + +One of the most immediate problems that occurs with legacy operating systems +is that the system timekeeping routines are often designed to keep track of +time by counting periodic interrupts. These interrupts may come from the PIT +or the RTC, but the problem is the same: the host virtualization engine may not +be able to deliver the proper number of interrupts per second, and so guest +time may fall behind. This is especially problematic if a high interrupt rate +is selected, such as 1000 HZ, which is unfortunately the default for many Linux +guests. + +There are three approaches to solving this problem; first, it may be possible +to simply ignore it. Guests which have a separate time source for tracking +'wall clock' or 'real time' may not need any adjustment of their interrupts to +maintain proper time. If this is not sufficient, it may be necessary to inject +additional interrupts into the guest in order to increase the effective +interrupt rate. This approach leads to complications in extreme conditions, +where host load or guest lag is too much to compensate for, and thus another +solution to the problem has risen: the guest may need to become aware of lost +ticks and compensate for them internally. Although promising in theory, the +implementation of this policy in Linux has been extremely error prone, and a +number of buggy variants of lost tick compensation are distributed across +commonly used Linux systems. + +Windows uses periodic RTC clocking as a means of keeping time internally, and +thus requires interrupt slewing to keep proper time. It does use a low enough +rate (ed: is it 18.2 Hz?) however that it has not yet been a problem in +practice. + +4.2) TSC sampling and serialization + +As the highest precision time source available, the cycle counter of the CPU +has aroused much interest from developers. As explained above, this timer has +many problems unique to its nature as a local, potentially unstable and +potentially unsynchronized source. One issue which is not unique to the TSC, +but is highlighted because of its very precise nature is sampling delay. By +definition, the counter, once read is already old. However, it is also +possible for the counter to be read ahead of the actual use of the result. +This is a consequence of the superscalar execution of the instruction stream, +which may execute instructions out of order. Such execution is called +non-serialized. Forcing serialized execution is necessary for precise +measurement with the TSC, and requires a serializing instruction, such as CPUID +or an MSR read. + +Since CPUID may actually be virtualized by a trap and emulate mechanism, this +serialization can pose a performance issue for hardware virtualization. An +accurate time stamp counter reading may therefore not always be available, and +it may be necessary for an implementation to guard against "backwards" reads of +the TSC as seen from other CPUs, even in an otherwise perfectly synchronized +system. + +4.3) Timespec aliasing + +Additionally, this lack of serialization from the TSC poses another challenge +when using results of the TSC when measured against another time source. As +the TSC is much higher precision, many possible values of the TSC may be read +while another clock is still expressing the same value. + +That is, you may read (T,T+10) while external clock C maintains the same value. +Due to non-serialized reads, you may actually end up with a range which +fluctuates - from (T-1.. T+10). Thus, any time calculated from a TSC, but +calibrated against an external value may have a range of valid values. +Re-calibrating this computation may actually cause time, as computed after the +calibration, to go backwards, compared with time computed before the +calibration. + +This problem is particularly pronounced with an internal time source in Linux, +the kernel time, which is expressed in the theoretically high resolution +timespec - but which advances in much larger granularity intervals, sometimes +at the rate of jiffies, and possibly in catchup modes, at a much larger step. + +This aliasing requires care in the computation and recalibration of kvmclock +and any other values derived from TSC computation (such as TSC virtualization +itself). + +4.4) Migration + +Migration of a virtual machine raises problems for timekeeping in two ways. +First, the migration itself may take time, during which interrupts cannot be +delivered, and after which, the guest time may need to be caught up. NTP may +be able to help to some degree here, as the clock correction required is +typically small enough to fall in the NTP-correctable window. + +An additional concern is that timers based off the TSC (or HPET, if the raw bus +clock is exposed) may now be running at different rates, requiring compensation +in some way in the hypervisor by virtualizing these timers. In addition, +migrating to a faster machine may preclude the use of a passthrough TSC, as a +faster clock cannot be made visible to a guest without the potential of time +advancing faster than usual. A slower clock is less of a problem, as it can +always be caught up to the original rate. KVM clock avoids these problems by +simply storing multipliers and offsets against the TSC for the guest to convert +back into nanosecond resolution values. + +4.5) Scheduling + +Since scheduling may be based on precise timing and firing of interrupts, the +scheduling algorithms of an operating system may be adversely affected by +virtualization. In theory, the effect is random and should be universally +distributed, but in contrived as well as real scenarios (guest device access, +causes of virtualization exits, possible context switch), this may not always +be the case. The effect of this has not been well studied. + +In an attempt to work around this, several implementations have provided a +paravirtualized scheduler clock, which reveals the true amount of CPU time for +which a virtual machine has been running. + +4.6) Watchdogs + +Watchdog timers, such as the lock detector in Linux may fire accidentally when +running under hardware virtualization due to timer interrupts being delayed or +misinterpretation of the passage of real time. Usually, these warnings are +spurious and can be ignored, but in some circumstances it may be necessary to +disable such detection. + +4.7) Delays and precision timing + +Precise timing and delays may not be possible in a virtualized system. This +can happen if the system is controlling physical hardware, or issues delays to +compensate for slower I/O to and from devices. The first issue is not solvable +in general for a virtualized system; hardware control software can't be +adequately virtualized without a full real-time operating system, which would +require an RT aware virtualization platform. + +The second issue may cause performance problems, but this is unlikely to be a +significant issue. In many cases these delays may be eliminated through +configuration or paravirtualization. + +4.8) Covert channels and leaks + +In addition to the above problems, time information will inevitably leak to the +guest about the host in anything but a perfect implementation of virtualized +time. This may allow the guest to infer the presence of a hypervisor (as in a +red-pill type detection), and it may allow information to leak between guests +by using CPU utilization itself as a signalling channel. Preventing such +problems would require completely isolated virtual time which may not track +real time any longer. This may be useful in certain security or QA contexts, +but in general isn't recommended for real-world deployment scenarios. -- cgit v1.2.3-70-g09d2 From cbe487fac7fc016dbabbcbe83f11384e1803a56d Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 3 Aug 2010 10:39:35 +0200 Subject: KVM: PPC: Add mtsrin PV code This is the guest side of the mtsr acceleration. Using this a guest can now call mtsrin with almost no overhead as long as it ensures that it only uses it with (MSR_IR|MSR_DR) == 0. Linux does that, so we're good. Signed-off-by: Alexander Graf --- Documentation/kvm/ppc-pv.txt | 3 ++ arch/powerpc/kernel/asm-offsets.c | 1 + arch/powerpc/kernel/kvm.c | 60 +++++++++++++++++++++++++++++++++++++++ arch/powerpc/kernel/kvm_emul.S | 50 ++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kvm/ppc-pv.txt b/Documentation/kvm/ppc-pv.txt index 41ee16d954d7..922cf954bd99 100644 --- a/Documentation/kvm/ppc-pv.txt +++ b/Documentation/kvm/ppc-pv.txt @@ -160,6 +160,9 @@ mtmsr rX b mtmsrd rX, 1 b +[Book3S only] +mtsrin rX, rY b + [BookE only] wrteei [0|1] b diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 6d92b4e13ebf..7f0d6fcc28a3 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -478,6 +478,7 @@ int main(void) DEFINE(KVM_MAGIC_MSR, offsetof(struct kvm_vcpu_arch_shared, msr)); DEFINE(KVM_MAGIC_CRITICAL, offsetof(struct kvm_vcpu_arch_shared, critical)); + DEFINE(KVM_MAGIC_SR, offsetof(struct kvm_vcpu_arch_shared, sr)); #endif #ifdef CONFIG_44x diff --git a/arch/powerpc/kernel/kvm.c b/arch/powerpc/kernel/kvm.c index 226882fe85a6..c8bab24ff8ac 100644 --- a/arch/powerpc/kernel/kvm.c +++ b/arch/powerpc/kernel/kvm.c @@ -42,6 +42,7 @@ #define KVM_INST_B_MAX 0x01ffffff #define KVM_MASK_RT 0x03e00000 +#define KVM_MASK_RB 0x0000f800 #define KVM_INST_MFMSR 0x7c0000a6 #define KVM_INST_MFSPR_SPRG0 0x7c1042a6 #define KVM_INST_MFSPR_SPRG1 0x7c1142a6 @@ -69,6 +70,8 @@ #define KVM_INST_WRTEEI_0 0x7c000146 #define KVM_INST_WRTEEI_1 0x7c008146 +#define KVM_INST_MTSRIN 0x7c0001e4 + static bool kvm_patching_worked = true; static char kvm_tmp[1024 * 1024]; static int kvm_tmp_index; @@ -264,6 +267,51 @@ static void kvm_patch_ins_wrteei(u32 *inst) #endif +#ifdef CONFIG_PPC_BOOK3S_32 + +extern u32 kvm_emulate_mtsrin_branch_offs; +extern u32 kvm_emulate_mtsrin_reg1_offs; +extern u32 kvm_emulate_mtsrin_reg2_offs; +extern u32 kvm_emulate_mtsrin_orig_ins_offs; +extern u32 kvm_emulate_mtsrin_len; +extern u32 kvm_emulate_mtsrin[]; + +static void kvm_patch_ins_mtsrin(u32 *inst, u32 rt, u32 rb) +{ + u32 *p; + int distance_start; + int distance_end; + ulong next_inst; + + p = kvm_alloc(kvm_emulate_mtsrin_len * 4); + if (!p) + return; + + /* Find out where we are and put everything there */ + distance_start = (ulong)p - (ulong)inst; + next_inst = ((ulong)inst + 4); + distance_end = next_inst - (ulong)&p[kvm_emulate_mtsrin_branch_offs]; + + /* Make sure we only write valid b instructions */ + if (distance_start > KVM_INST_B_MAX) { + kvm_patching_worked = false; + return; + } + + /* Modify the chunk to fit the invocation */ + memcpy(p, kvm_emulate_mtsrin, kvm_emulate_mtsrin_len * 4); + p[kvm_emulate_mtsrin_branch_offs] |= distance_end & KVM_INST_B_MASK; + p[kvm_emulate_mtsrin_reg1_offs] |= (rb << 10); + p[kvm_emulate_mtsrin_reg2_offs] |= rt; + p[kvm_emulate_mtsrin_orig_ins_offs] = *inst; + flush_icache_range((ulong)p, (ulong)p + kvm_emulate_mtsrin_len * 4); + + /* Patch the invocation */ + kvm_patch_ins_b(inst, distance_start); +} + +#endif + static void kvm_map_magic_page(void *data) { u32 *features = data; @@ -360,6 +408,18 @@ static void kvm_check_ins(u32 *inst, u32 features) break; } + switch (inst_no_rt & ~KVM_MASK_RB) { +#ifdef CONFIG_PPC_BOOK3S_32 + case KVM_INST_MTSRIN: + if (features & KVM_MAGIC_FEAT_SR) { + u32 inst_rb = _inst & KVM_MASK_RB; + kvm_patch_ins_mtsrin(inst, inst_rt, inst_rb); + } + break; + break; +#endif + } + switch (_inst) { #ifdef CONFIG_BOOKE case KVM_INST_WRTEEI_0: diff --git a/arch/powerpc/kernel/kvm_emul.S b/arch/powerpc/kernel/kvm_emul.S index 3199f65ede2c..a6e97e7a55e0 100644 --- a/arch/powerpc/kernel/kvm_emul.S +++ b/arch/powerpc/kernel/kvm_emul.S @@ -245,3 +245,53 @@ kvm_emulate_wrteei_ee_offs: .global kvm_emulate_wrteei_len kvm_emulate_wrteei_len: .long (kvm_emulate_wrteei_end - kvm_emulate_wrteei) / 4 + + +.global kvm_emulate_mtsrin +kvm_emulate_mtsrin: + + SCRATCH_SAVE + + LL64(r31, KVM_MAGIC_PAGE + KVM_MAGIC_MSR, 0) + andi. r31, r31, MSR_DR | MSR_IR + beq kvm_emulate_mtsrin_reg1 + + SCRATCH_RESTORE + +kvm_emulate_mtsrin_orig_ins: + nop + b kvm_emulate_mtsrin_branch + +kvm_emulate_mtsrin_reg1: + /* rX >> 26 */ + rlwinm r30,r0,6,26,29 + +kvm_emulate_mtsrin_reg2: + stw r0, (KVM_MAGIC_PAGE + KVM_MAGIC_SR)(r30) + + SCRATCH_RESTORE + + /* Go back to caller */ +kvm_emulate_mtsrin_branch: + b . +kvm_emulate_mtsrin_end: + +.global kvm_emulate_mtsrin_branch_offs +kvm_emulate_mtsrin_branch_offs: + .long (kvm_emulate_mtsrin_branch - kvm_emulate_mtsrin) / 4 + +.global kvm_emulate_mtsrin_reg1_offs +kvm_emulate_mtsrin_reg1_offs: + .long (kvm_emulate_mtsrin_reg1 - kvm_emulate_mtsrin) / 4 + +.global kvm_emulate_mtsrin_reg2_offs +kvm_emulate_mtsrin_reg2_offs: + .long (kvm_emulate_mtsrin_reg2 - kvm_emulate_mtsrin) / 4 + +.global kvm_emulate_mtsrin_orig_ins_offs +kvm_emulate_mtsrin_orig_ins_offs: + .long (kvm_emulate_mtsrin_orig_ins - kvm_emulate_mtsrin) / 4 + +.global kvm_emulate_mtsrin_len +kvm_emulate_mtsrin_len: + .long (kvm_emulate_mtsrin_end - kvm_emulate_mtsrin) / 4 -- cgit v1.2.3-70-g09d2 From 6f7a2bd41fa8d52cbf5f32fdf8ba659d4ce4ae59 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 31 Aug 2010 02:03:32 +0200 Subject: KVM: PPC: Document KVM_INTERRUPT ioctl This adds some documentation for the KVM_INTERRUPT special cases that PowerPC now implements. Signed-off-by: Alexander Graf --- Documentation/kvm/api.txt | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kvm/api.txt b/Documentation/kvm/api.txt index 44d9893f9db1..24d634128746 100644 --- a/Documentation/kvm/api.txt +++ b/Documentation/kvm/api.txt @@ -320,13 +320,13 @@ struct kvm_translation { 4.15 KVM_INTERRUPT Capability: basic -Architectures: x86 +Architectures: x86, ppc Type: vcpu ioctl Parameters: struct kvm_interrupt (in) Returns: 0 on success, -1 on error Queues a hardware interrupt vector to be injected. This is only -useful if in-kernel local APIC is not used. +useful if in-kernel local APIC or equivalent is not used. /* for KVM_INTERRUPT */ struct kvm_interrupt { @@ -334,8 +334,37 @@ struct kvm_interrupt { __u32 irq; }; +X86: + Note 'irq' is an interrupt vector, not an interrupt pin or line. +PPC: + +Queues an external interrupt to be injected. This ioctl is overleaded +with 3 different irq values: + +a) KVM_INTERRUPT_SET + + This injects an edge type external interrupt into the guest once it's ready + to receive interrupts. When injected, the interrupt is done. + +b) KVM_INTERRUPT_UNSET + + This unsets any pending interrupt. + + Only available with KVM_CAP_PPC_UNSET_IRQ. + +c) KVM_INTERRUPT_SET_LEVEL + + This injects a level type external interrupt into the guest context. The + interrupt stays pending until a specific ioctl with KVM_INTERRUPT_UNSET + is triggered. + + Only available with KVM_CAP_PPC_IRQ_LEVEL. + +Note that any value for 'irq' other than the ones stated above is invalid +and incurs unexpected behavior. + 4.16 KVM_DEBUG_GUEST Capability: basic -- cgit v1.2.3-70-g09d2 From d1e87c7ee65a20b10faf7e59dbe2cc934c32473b Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 31 Aug 2010 04:25:39 +0200 Subject: KVM: PPC: Add documentation for magic page enhancements This documents how to detect additional features inside the magic page when a guest maps it. Signed-off-by: Alexander Graf --- Documentation/kvm/ppc-pv.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kvm/ppc-pv.txt b/Documentation/kvm/ppc-pv.txt index 922cf954bd99..a7f2244b3be9 100644 --- a/Documentation/kvm/ppc-pv.txt +++ b/Documentation/kvm/ppc-pv.txt @@ -102,6 +102,20 @@ struct kvm_vcpu_arch_shared { Additions to the page must only occur at the end. Struct fields are always 32 or 64 bit aligned, depending on them being 32 or 64 bit wide respectively. +Magic page features +=================== + +When mapping the magic page using the KVM hypercall KVM_HC_PPC_MAP_MAGIC_PAGE, +a second return value is passed to the guest. This second return value contains +a bitmap of available features inside the magic page. + +The following enhancements to the magic page are currently available: + + KVM_MAGIC_FEAT_SR Maps SR registers r/w in the magic page + +For enhanced features in the magic page, please check for the existence of the +feature before using them! + MSR bits ======== -- cgit v1.2.3-70-g09d2 From c39cbd2a0012334714409eec1e9cf4d542e359e9 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sun, 12 Sep 2010 16:39:11 +0200 Subject: KVM: Document that KVM_GET_SUPPORTED_CPUID may return emulated values Signed-off-by: Avi Kivity --- Documentation/kvm/api.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kvm/api.txt b/Documentation/kvm/api.txt index 24d634128746..b336266bea5e 100644 --- a/Documentation/kvm/api.txt +++ b/Documentation/kvm/api.txt @@ -1042,8 +1042,9 @@ number is just right, the 'nent' field is adjusted to the number of valid entries in the 'entries' array, which is then filled. The entries returned are the host cpuid as returned by the cpuid instruction, -with unknown or unsupported features masked out. The fields in each entry -are defined as follows: +with unknown or unsupported features masked out. Some features (for example, +x2apic), may not be present in the host cpu, but are exposed by kvm if it can +emulate them efficiently. The fields in each entry are defined as follows: function: the eax value used to obtain the entry index: the ecx value used to obtain the entry (for entries that are -- cgit v1.2.3-70-g09d2 From 8475f94abfa40d6975923ff022280cf7250fc5fb Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Mon, 20 Sep 2010 22:16:45 +0800 Subject: KVM: fix the description of kvm-amd.nested in documentation The default state of 'kvm-amd.nested' is enabled now, so fix the documentation Signed-off-by: Xiao Guangrong Signed-off-by: Marcelo Tosatti --- Documentation/kernel-parameters.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 6bd23751a2ce..3170edbf1924 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1127,7 +1127,7 @@ and is between 256 and 4096 characters. It is defined in the file Default is 1 (enabled) kvm-amd.nested= [KVM,AMD] Allow nested virtualization in KVM/SVM. - Default is 0 (off) + Default is 1 (enabled) kvm-amd.npt= [KVM,AMD] Disable nested paging (virtualized MMU) for all guests. -- cgit v1.2.3-70-g09d2 From a182d8737f0bdfb684b3255c8e266e71999e2225 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Mon, 20 Sep 2010 22:17:48 +0800 Subject: KVM: document 'kvm.mmu_audit' parameter Document this parameter into Documentation/kernel-parameters.txt Signed-off-by: Xiao Guangrong Signed-off-by: Marcelo Tosatti --- Documentation/kernel-parameters.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 3170edbf1924..8dc254829482 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1126,6 +1126,10 @@ and is between 256 and 4096 characters. It is defined in the file kvm.oos_shadow= [KVM] Disable out-of-sync shadow paging. Default is 1 (enabled) + kvm.mmu_audit= [KVM] This is a R/W parameter which allows audit + KVM MMU at runtime. + Default is 0 (off) + kvm-amd.nested= [KVM,AMD] Allow nested virtualization in KVM/SVM. Default is 1 (enabled) -- cgit v1.2.3-70-g09d2 From 02c35fca7cf4ea2dfdc6db279e230cacbbf4b870 Mon Sep 17 00:00:00 2001 From: Fred Isaman Date: Wed, 20 Oct 2010 00:17:59 -0400 Subject: NFSv4.1: pnfs: full mount/umount infrastructure Allow a module implementing a layout type to register, and have its mount/umount routines called for filesystems that the server declares support it. Signed-off-by: Fred Isaman Signed-off-by: Marc Eshel Signed-off-by: Andy Adamson Signed-off-by: Bian Naimeng Signed-off-by: Benny Halevy Signed-off-by: Trond Myklebust --- Documentation/filesystems/nfs/00-INDEX | 2 + Documentation/filesystems/nfs/pnfs.txt | 48 +++++++++++++++++++ fs/nfs/Kconfig | 8 +++- fs/nfs/pnfs.c | 88 +++++++++++++++++++++++++++++++++- fs/nfs/pnfs.h | 9 ++++ 5 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 Documentation/filesystems/nfs/pnfs.txt (limited to 'Documentation') diff --git a/Documentation/filesystems/nfs/00-INDEX b/Documentation/filesystems/nfs/00-INDEX index 3225a5662114..a57e12411d2a 100644 --- a/Documentation/filesystems/nfs/00-INDEX +++ b/Documentation/filesystems/nfs/00-INDEX @@ -12,6 +12,8 @@ nfs-rdma.txt - how to install and setup the Linux NFS/RDMA client and server software nfsroot.txt - short guide on setting up a diskless box with NFS root filesystem. +pnfs.txt + - short explanation of some of the internals of the pnfs client code rpc-cache.txt - introduction to the caching mechanisms in the sunrpc layer. idmapper.txt diff --git a/Documentation/filesystems/nfs/pnfs.txt b/Documentation/filesystems/nfs/pnfs.txt new file mode 100644 index 000000000000..bc0b9cfe095b --- /dev/null +++ b/Documentation/filesystems/nfs/pnfs.txt @@ -0,0 +1,48 @@ +Reference counting in pnfs: +========================== + +The are several inter-related caches. We have layouts which can +reference multiple devices, each of which can reference multiple data servers. +Each data server can be referenced by multiple devices. Each device +can be referenced by multiple layouts. To keep all of this straight, +we need to reference count. + + +struct pnfs_layout_hdr +---------------------- +The on-the-wire command LAYOUTGET corresponds to struct +pnfs_layout_segment, usually referred to by the variable name lseg. +Each nfs_inode may hold a pointer to a cache of of these layout +segments in nfsi->layout, of type struct pnfs_layout_hdr. + +We reference the header for the inode pointing to it, across each +outstanding RPC call that references it (LAYOUTGET, LAYOUTRETURN, +LAYOUTCOMMIT), and for each lseg held within. + +Each header is also (when non-empty) put on a list associated with +struct nfs_client (cl_layouts). Being put on this list does not bump +the reference count, as the layout is kept around by the lseg that +keeps it in the list. + +deviceid_cache +-------------- +lsegs reference device ids, which are resolved per nfs_client and +layout driver type. The device ids are held in a RCU cache (struct +nfs4_deviceid_cache). The cache itself is referenced across each +mount. The entries (struct nfs4_deviceid) themselves are held across +the lifetime of each lseg referencing them. + +RCU is used because the deviceid is basically a write once, read many +data structure. The hlist size of 32 buckets needs better +justification, but seems reasonable given that we can have multiple +deviceid's per filesystem, and multiple filesystems per nfs_client. + +The hash code is copied from the nfsd code base. A discussion of +hashing and variations of this algorithm can be found at: +http://groups.google.com/group/comp.lang.c/browse_thread/thread/9522965e2b8d3809 + +data server cache +----------------- +file driver devices refer to data servers, which are kept in a module +level cache. Its reference is held over the lifetime of the deviceid +pointing to it. diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 3f69752d6f18..d94311943ec6 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -75,13 +75,17 @@ config NFS_V4 config NFS_V4_1 bool "NFS client support for NFSv4.1 (EXPERIMENTAL)" - depends on NFS_V4 && EXPERIMENTAL + depends on NFS_FS && NFS_V4 && EXPERIMENTAL + select PNFS_FILE_LAYOUT help This option enables support for minor version 1 of the NFSv4 protocol - (draft-ietf-nfsv4-minorversion1) in the kernel's NFS client. + (RFC 5661) in the kernel's NFS client. If unsure, say N. +config PNFS_FILE_LAYOUT + tristate + config ROOT_NFS bool "Root file system on NFS" depends on NFS_FS=y && IP_PNP diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index b483026e82aa..cf795625610e 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -32,16 +32,51 @@ #define NFSDBG_FACILITY NFSDBG_PNFS -/* STUB that returns the equivalent of "no module found" */ +/* Locking: + * + * pnfs_spinlock: + * protects pnfs_modules_tbl. + */ +static DEFINE_SPINLOCK(pnfs_spinlock); + +/* + * pnfs_modules_tbl holds all pnfs modules + */ +static LIST_HEAD(pnfs_modules_tbl); + +/* Return the registered pnfs layout driver module matching given id */ +static struct pnfs_layoutdriver_type * +find_pnfs_driver_locked(u32 id) +{ + struct pnfs_layoutdriver_type *local; + + list_for_each_entry(local, &pnfs_modules_tbl, pnfs_tblid) + if (local->id == id) + goto out; + local = NULL; +out: + dprintk("%s: Searching for id %u, found %p\n", __func__, id, local); + return local; +} + static struct pnfs_layoutdriver_type * find_pnfs_driver(u32 id) { - return NULL; + struct pnfs_layoutdriver_type *local; + + spin_lock(&pnfs_spinlock); + local = find_pnfs_driver_locked(id); + spin_unlock(&pnfs_spinlock); + return local; } void unset_pnfs_layoutdriver(struct nfs_server *nfss) { + if (nfss->pnfs_curr_ld) { + nfss->pnfs_curr_ld->uninitialize_mountpoint(nfss); + module_put(nfss->pnfs_curr_ld->owner); + } nfss->pnfs_curr_ld = NULL; } @@ -74,7 +109,18 @@ set_pnfs_layoutdriver(struct nfs_server *server, u32 id) goto out_no_driver; } } + if (!try_module_get(ld_type->owner)) { + dprintk("%s: Could not grab reference on module\n", __func__); + goto out_no_driver; + } server->pnfs_curr_ld = ld_type; + if (ld_type->initialize_mountpoint(server)) { + printk(KERN_ERR + "%s: Error initializing mount point for layout driver %u.\n", + __func__, id); + module_put(ld_type->owner); + goto out_no_driver; + } dprintk("%s: pNFS module for %u set\n", __func__, id); return; @@ -82,3 +128,41 @@ out_no_driver: dprintk("%s: Using NFSv4 I/O\n", __func__); server->pnfs_curr_ld = NULL; } + +int +pnfs_register_layoutdriver(struct pnfs_layoutdriver_type *ld_type) +{ + int status = -EINVAL; + struct pnfs_layoutdriver_type *tmp; + + if (ld_type->id == 0) { + printk(KERN_ERR "%s id 0 is reserved\n", __func__); + return status; + } + + spin_lock(&pnfs_spinlock); + tmp = find_pnfs_driver_locked(ld_type->id); + if (!tmp) { + list_add(&ld_type->pnfs_tblid, &pnfs_modules_tbl); + status = 0; + dprintk("%s Registering id:%u name:%s\n", __func__, ld_type->id, + ld_type->name); + } else { + printk(KERN_ERR "%s Module with id %d already loaded!\n", + __func__, ld_type->id); + } + spin_unlock(&pnfs_spinlock); + + return status; +} +EXPORT_SYMBOL_GPL(pnfs_register_layoutdriver); + +void +pnfs_unregister_layoutdriver(struct pnfs_layoutdriver_type *ld_type) +{ + dprintk("%s Deregistering id:%u\n", __func__, ld_type->id); + spin_lock(&pnfs_spinlock); + list_del(&ld_type->pnfs_tblid); + spin_unlock(&pnfs_spinlock); +} +EXPORT_SYMBOL_GPL(pnfs_unregister_layoutdriver); diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index c628ef131d83..61531f338576 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -36,8 +36,17 @@ /* Per-layout driver specific registration structure */ struct pnfs_layoutdriver_type { + struct list_head pnfs_tblid; + const u32 id; + const char *name; + struct module *owner; + int (*initialize_mountpoint) (struct nfs_server *); + int (*uninitialize_mountpoint) (struct nfs_server *); }; +extern int pnfs_register_layoutdriver(struct pnfs_layoutdriver_type *); +extern void pnfs_unregister_layoutdriver(struct pnfs_layoutdriver_type *); + void set_pnfs_layoutdriver(struct nfs_server *, u32 id); void unset_pnfs_layoutdriver(struct nfs_server *); -- cgit v1.2.3-70-g09d2 From 89ff05ec553f3e70b8773c501da01bf7ad952cab Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Thu, 21 Oct 2010 08:37:41 +0000 Subject: phylib: make local function static The following functions are not used directly by any drivers: phy_attach_direct phy_device_create phy_prepare_link genphy_config_advert genphy_setup_forced phy_config_interrupt phy_clear_interrypt phy_sanitize_settings phy_enable_interrupts phy_disable_interrupts Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- Documentation/networking/phy.txt | 18 ------------------ drivers/net/phy/phy.c | 13 +++++-------- drivers/net/phy/phy_device.c | 19 ++++++++++--------- include/linux/phy.h | 12 ------------ 4 files changed, 15 insertions(+), 47 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt index 88bb71b46da4..9eb1ba52013d 100644 --- a/Documentation/networking/phy.txt +++ b/Documentation/networking/phy.txt @@ -177,18 +177,6 @@ Doing it all yourself A convenience function to print out the PHY status neatly. - int phy_clear_interrupt(struct phy_device *phydev); - int phy_config_interrupt(struct phy_device *phydev, u32 interrupts); - - Clear the PHY's interrupt, and configure which ones are allowed, - respectively. Currently only supports all on, or all off. - - int phy_enable_interrupts(struct phy_device *phydev); - int phy_disable_interrupts(struct phy_device *phydev); - - Functions which enable/disable PHY interrupts, clearing them - before and after, respectively. - int phy_start_interrupts(struct phy_device *phydev); int phy_stop_interrupts(struct phy_device *phydev); @@ -213,12 +201,6 @@ Doing it all yourself Fills the phydev structure with up-to-date information about the current settings in the PHY. - void phy_sanitize_settings(struct phy_device *phydev) - - Resolves differences between currently desired settings, and - supported settings for the given PHY device. Does not make - the changes in the hardware, though. - int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd); int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd); diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 1bb16cb79433..7670aac0e93f 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -65,7 +65,7 @@ EXPORT_SYMBOL(phy_print_status); * * Returns 0 on success on < 0 on error. */ -int phy_clear_interrupt(struct phy_device *phydev) +static int phy_clear_interrupt(struct phy_device *phydev) { int err = 0; @@ -82,7 +82,7 @@ int phy_clear_interrupt(struct phy_device *phydev) * * Returns 0 on success on < 0 on error. */ -int phy_config_interrupt(struct phy_device *phydev, u32 interrupts) +static int phy_config_interrupt(struct phy_device *phydev, u32 interrupts) { int err = 0; @@ -208,7 +208,7 @@ static inline int phy_find_valid(int idx, u32 features) * duplexes. Drop down by one in this order: 1000/FULL, * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF. */ -void phy_sanitize_settings(struct phy_device *phydev) +static void phy_sanitize_settings(struct phy_device *phydev) { u32 features = phydev->supported; int idx; @@ -223,7 +223,6 @@ void phy_sanitize_settings(struct phy_device *phydev) phydev->speed = settings[idx].speed; phydev->duplex = settings[idx].duplex; } -EXPORT_SYMBOL(phy_sanitize_settings); /** * phy_ethtool_sset - generic ethtool sset function, handles all the details @@ -532,7 +531,7 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat) * phy_enable_interrupts - Enable the interrupts from the PHY side * @phydev: target phy_device struct */ -int phy_enable_interrupts(struct phy_device *phydev) +static int phy_enable_interrupts(struct phy_device *phydev) { int err; @@ -545,13 +544,12 @@ int phy_enable_interrupts(struct phy_device *phydev) return err; } -EXPORT_SYMBOL(phy_enable_interrupts); /** * phy_disable_interrupts - Disable the PHY interrupts from the PHY side * @phydev: target phy_device struct */ -int phy_disable_interrupts(struct phy_device *phydev) +static int phy_disable_interrupts(struct phy_device *phydev) { int err; @@ -574,7 +572,6 @@ phy_err: return err; } -EXPORT_SYMBOL(phy_disable_interrupts); /** * phy_start_interrupts - request and enable interrupts for a PHY device diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 16ddc77313cb..993c52c82aeb 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -57,6 +57,9 @@ extern void mdio_bus_exit(void); static LIST_HEAD(phy_fixup_list); static DEFINE_MUTEX(phy_fixup_lock); +static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, + u32 flags, phy_interface_t interface); + /* * Creates a new phy_fixup and adds it to the list * @bus_id: A string which matches phydev->dev.bus_id (or PHY_ANY_ID) @@ -146,7 +149,8 @@ int phy_scan_fixups(struct phy_device *phydev) } EXPORT_SYMBOL(phy_scan_fixups); -struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) +static struct phy_device* phy_device_create(struct mii_bus *bus, + int addr, int phy_id) { struct phy_device *dev; @@ -193,7 +197,6 @@ struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) return dev; } -EXPORT_SYMBOL(phy_device_create); /** * get_phy_id - reads the specified addr for its ID. @@ -316,7 +319,7 @@ EXPORT_SYMBOL(phy_find_first); * If you want to monitor your own link state, don't call * this function. */ -void phy_prepare_link(struct phy_device *phydev, +static void phy_prepare_link(struct phy_device *phydev, void (*handler)(struct net_device *)) { phydev->adjust_link = handler; @@ -435,8 +438,8 @@ int phy_init_hw(struct phy_device *phydev) * the attaching device, and given a callback for link status * change. The phy_device is returned to the attaching driver. */ -int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, - u32 flags, phy_interface_t interface) +static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, + u32 flags, phy_interface_t interface) { struct device *d = &phydev->dev; @@ -473,7 +476,6 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, * (dev_flags and interface) */ return phy_init_hw(phydev); } -EXPORT_SYMBOL(phy_attach_direct); /** * phy_attach - attach a network device to a particular PHY device @@ -540,7 +542,7 @@ EXPORT_SYMBOL(phy_detach); * what is supported. Returns < 0 on error, 0 if the PHY's advertisement * hasn't changed, and > 0 if it has changed. */ -int genphy_config_advert(struct phy_device *phydev) +static int genphy_config_advert(struct phy_device *phydev) { u32 advertise; int oldadv, adv; @@ -605,7 +607,6 @@ int genphy_config_advert(struct phy_device *phydev) return changed; } -EXPORT_SYMBOL(genphy_config_advert); /** * genphy_setup_forced - configures/forces speed/duplex from @phydev @@ -615,7 +616,7 @@ EXPORT_SYMBOL(genphy_config_advert); * to the values in phydev. Assumes that the values are valid. * Please see phy_sanitize_settings(). */ -int genphy_setup_forced(struct phy_device *phydev) +static int genphy_setup_forced(struct phy_device *phydev) { int err; int ctl = 0; diff --git a/include/linux/phy.h b/include/linux/phy.h index a6e047a04f79..7da5fa845959 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -472,11 +472,7 @@ static inline int phy_write(struct phy_device *phydev, u32 regnum, u16 val) int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id); struct phy_device* get_phy_device(struct mii_bus *bus, int addr); int phy_device_register(struct phy_device *phy); -int phy_clear_interrupt(struct phy_device *phydev); -int phy_config_interrupt(struct phy_device *phydev, u32 interrupts); int phy_init_hw(struct phy_device *phydev); -int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, - u32 flags, phy_interface_t interface); struct phy_device * phy_attach(struct net_device *dev, const char *bus_id, u32 flags, phy_interface_t interface); struct phy_device *phy_find_first(struct mii_bus *bus); @@ -492,17 +488,12 @@ void phy_start(struct phy_device *phydev); void phy_stop(struct phy_device *phydev); int phy_start_aneg(struct phy_device *phydev); -void phy_sanitize_settings(struct phy_device *phydev); int phy_stop_interrupts(struct phy_device *phydev); -int phy_enable_interrupts(struct phy_device *phydev); -int phy_disable_interrupts(struct phy_device *phydev); static inline int phy_read_status(struct phy_device *phydev) { return phydev->drv->read_status(phydev); } -int genphy_config_advert(struct phy_device *phydev); -int genphy_setup_forced(struct phy_device *phydev); int genphy_restart_aneg(struct phy_device *phydev); int genphy_config_aneg(struct phy_device *phydev); int genphy_update_link(struct phy_device *phydev); @@ -511,8 +502,6 @@ int genphy_suspend(struct phy_device *phydev); int genphy_resume(struct phy_device *phydev); void phy_driver_unregister(struct phy_driver *drv); int phy_driver_register(struct phy_driver *new_driver); -void phy_prepare_link(struct phy_device *phydev, - void (*adjust_link)(struct net_device *)); void phy_state_machine(struct work_struct *work); void phy_start_machine(struct phy_device *phydev, void (*handler)(struct net_device *)); @@ -523,7 +512,6 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd); int phy_start_interrupts(struct phy_device *phydev); void phy_print_status(struct phy_device *phydev); -struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id); void phy_device_free(struct phy_device *phydev); int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, -- cgit v1.2.3-70-g09d2 From 5e71b7a64cb4c6cff75ca42b535d8227526ec592 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Fri, 17 Sep 2010 21:19:57 -0400 Subject: mmc: make number of mmcblk minors configurable The old limit of number of minor numbers per mmcblk device was hardcoded at 8. This isn't enough for some of the more elaborate partitioning schemes, for example those used by Chrome OS. Since there might be a bunch of systems out there with static /dev contents that relies on the old numbering scheme, let's make it a build-time option with the default set to the previous 8. Also provide a boot/modprobe-time parameter to override the config default: mmcblk.perdev_minors. Signed-off-by: Olof Johansson Cc: Mandeep Baines Cc: Signed-off-by: Andrew Morton Signed-off-by: Chris Ball --- Documentation/devices.txt | 6 ++++++ drivers/mmc/card/Kconfig | 17 +++++++++++++++++ drivers/mmc/card/block.c | 43 +++++++++++++++++++++++++++++++------------ 3 files changed, 54 insertions(+), 12 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devices.txt b/Documentation/devices.txt index c58abf1ccc71..170cc1e7e135 100644 --- a/Documentation/devices.txt +++ b/Documentation/devices.txt @@ -2520,6 +2520,12 @@ Your cooperation is appreciated. 8 = /dev/mmcblk1 Second SD/MMC card ... + The start of next SD/MMC card can be configured with + CONFIG_MMC_BLOCK_MINORS, or overridden at boot/modprobe + time using the mmcblk.perdev_minors option. That would + bump the offset between each card to be the configured + value instead of the default 8. + 179 char CCube DVXChip-based PCI products 0 = /dev/dvxirq0 First DVX device 1 = /dev/dvxirq1 Second DVX device diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig index 3f2a912659af..57e4416b9ef0 100644 --- a/drivers/mmc/card/Kconfig +++ b/drivers/mmc/card/Kconfig @@ -14,6 +14,23 @@ config MMC_BLOCK mount the filesystem. Almost everyone wishing MMC support should say Y or M here. +config MMC_BLOCK_MINORS + int "Number of minors per block device" + range 4 256 + default 8 + help + Number of minors per block device. One is needed for every + partition on the disk (plus one for the whole disk). + + Number of total MMC minors available is 256, so your number + of supported block devices will be limited to 256 divided + by this number. + + Default is 8 to be backwards compatible with previous + hardwired device numbering. + + If unsure, say 8 here. + config MMC_BLOCK_BOUNCE bool "Use bounce buffer for simple hosts" depends on MMC_BLOCK diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index a9970504cabb..217f82037fc1 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -43,15 +43,27 @@ #include "queue.h" MODULE_ALIAS("mmc:block"); +#ifdef MODULE_PARAM_PREFIX +#undef MODULE_PARAM_PREFIX +#endif +#define MODULE_PARAM_PREFIX "mmcblk." + +static DEFINE_MUTEX(block_mutex); /* - * max 8 partitions per card + * The defaults come from config options but can be overriden by module + * or bootarg options. */ -#define MMC_SHIFT 3 -#define MMC_NUM_MINORS (256 >> MMC_SHIFT) +static int perdev_minors = CONFIG_MMC_BLOCK_MINORS; -static DEFINE_MUTEX(block_mutex); -static DECLARE_BITMAP(dev_use, MMC_NUM_MINORS); +/* + * We've only got one major, so number of mmcblk devices is + * limited to 256 / number of minors per device. + */ +static int max_devices; + +/* 256 minors, so at most 256 separate devices */ +static DECLARE_BITMAP(dev_use, 256); /* * There is one mmc_blk_data per slot. @@ -67,6 +79,9 @@ struct mmc_blk_data { static DEFINE_MUTEX(open_lock); +module_param(perdev_minors, int, 0444); +MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device"); + static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) { struct mmc_blk_data *md; @@ -88,10 +103,10 @@ static void mmc_blk_put(struct mmc_blk_data *md) md->usage--; if (md->usage == 0) { int devmaj = MAJOR(disk_devt(md->disk)); - int devidx = MINOR(disk_devt(md->disk)) >> MMC_SHIFT; + int devidx = MINOR(disk_devt(md->disk)) / perdev_minors; if (!devmaj) - devidx = md->disk->first_minor >> MMC_SHIFT; + devidx = md->disk->first_minor / perdev_minors; blk_cleanup_queue(md->queue.queue); @@ -566,8 +581,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) struct mmc_blk_data *md; int devidx, ret; - devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS); - if (devidx >= MMC_NUM_MINORS) + devidx = find_first_zero_bit(dev_use, max_devices); + if (devidx >= max_devices) return ERR_PTR(-ENOSPC); __set_bit(devidx, dev_use); @@ -584,7 +599,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) */ md->read_only = mmc_blk_readonly(card); - md->disk = alloc_disk(1 << MMC_SHIFT); + md->disk = alloc_disk(perdev_minors); if (md->disk == NULL) { ret = -ENOMEM; goto err_kfree; @@ -601,7 +616,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) md->queue.data = md; md->disk->major = MMC_BLOCK_MAJOR; - md->disk->first_minor = devidx << MMC_SHIFT; + md->disk->first_minor = devidx * perdev_minors; md->disk->fops = &mmc_bdops; md->disk->private_data = md; md->disk->queue = md->queue.queue; @@ -670,7 +685,6 @@ static int mmc_blk_probe(struct mmc_card *card) { struct mmc_blk_data *md; int err; - char cap_str[10]; /* @@ -760,6 +774,11 @@ static int __init mmc_blk_init(void) { int res; + if (perdev_minors != CONFIG_MMC_BLOCK_MINORS) + pr_info("mmcblk: using %d minors per device\n", perdev_minors); + + max_devices = 256 / perdev_minors; + res = register_blkdev(MMC_BLOCK_MAJOR, "mmc"); if (res) goto out; -- cgit v1.2.3-70-g09d2 From c9af3fa9e1e3e5154649991a14b74f3a2dee19ee Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 25 Oct 2010 16:10:43 +0200 Subject: [S390] topology: change default Switch default value of the kernel parameter 'topology' from off to on. Various performance measurements have finally shown that there are no (known) regressions anywhere. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- Documentation/kernel-parameters.txt | 2 +- arch/s390/kernel/topology.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 0b6815504e6d..b660085dcc69 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2438,7 +2438,7 @@ and is between 256 and 4096 characters. It is defined in the file topology informations if the hardware supports these. The scheduler will make use of these informations and e.g. base its process migration decisions on it. - Default is off. + Default is on. tp720= [HW,PS2] diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c index 13559c993847..eb0bc4752ae8 100644 --- a/arch/s390/kernel/topology.c +++ b/arch/s390/kernel/topology.c @@ -63,7 +63,7 @@ struct mask_info { cpumask_t mask; }; -static int topology_enabled; +static int topology_enabled = 1; static void topology_work_fn(struct work_struct *work); static struct tl_info *tl_info; static int machine_has_topology; @@ -311,9 +311,9 @@ static void set_topology_timer(void) static int __init early_parse_topology(char *p) { - if (strncmp(p, "on", 2)) + if (strncmp(p, "off", 3)) return 0; - topology_enabled = 1; + topology_enabled = 0; return 0; } early_param("topology", early_parse_topology); -- cgit v1.2.3-70-g09d2 From e5f5c99a39375ce533aacfdfb269978070121e1c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 25 Jun 2010 11:59:54 -0700 Subject: hwmon: LTC4261 Hardware monitoring driver This driver adds support for Linear Technology LTC4261 I2C Negative Voltage Hot Swap Controller. Reviewed-by: Ira W. Snyder Reviewed-by: Tom Grennan Signed-off-by: Guenter Roeck --- Documentation/hwmon/ltc4261 | 63 +++++++++ MAINTAINERS | 7 + drivers/hwmon/Kconfig | 11 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/ltc4261.c | 315 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 397 insertions(+) create mode 100644 Documentation/hwmon/ltc4261 create mode 100644 drivers/hwmon/ltc4261.c (limited to 'Documentation') diff --git a/Documentation/hwmon/ltc4261 b/Documentation/hwmon/ltc4261 new file mode 100644 index 000000000000..eba2e2c4b94d --- /dev/null +++ b/Documentation/hwmon/ltc4261 @@ -0,0 +1,63 @@ +Kernel driver ltc4261 +===================== + +Supported chips: + * Linear Technology LTC4261 + Prefix: 'ltc4261' + Addresses scanned: - + Datasheet: + http://cds.linear.com/docs/Datasheet/42612fb.pdf + +Author: Guenter Roeck + + +Description +----------- + +The LTC4261/LTC4261-2 negative voltage Hot Swap controllers allow a board +to be safely inserted and removed from a live backplane. + + +Usage Notes +----------- + +This driver does not probe for LTC4261 devices, since there is no register +which can be safely used to identify the chip. You will have to instantiate +the devices explicitly. + +Example: the following will load the driver for an LTC4261 at address 0x10 +on I2C bus #1: +$ modprobe ltc4261 +$ echo ltc4261 0x10 > /sys/bus/i2c/devices/i2c-1/new_device + + +Sysfs entries +------------- + +Voltage readings provided by this driver are reported as obtained from the ADC +registers. If a set of voltage divider resistors is installed, calculate the +real voltage by multiplying the reported value with (R1+R2)/R2, where R1 is the +value of the divider resistor against the measured voltage and R2 is the value +of the divider resistor against Ground. + +Current reading provided by this driver is reported as obtained from the ADC +Current Sense register. The reported value assumes that a 1 mOhm sense resistor +is installed. If a different sense resistor is installed, calculate the real +current by dividing the reported value by the sense resistor value in mOhm. + +The chip has two voltage sensors, but only one set of voltage alarm status bits. +In many many designs, those alarms are associated with the ADIN2 sensor, due to +the proximity of the ADIN2 pin to the OV pin. ADIN2 is, however, not available +on all chip variants. To ensure that the alarm condition is reported to the user, +report it with both voltage sensors. + +in1_input ADIN2 voltage (mV) +in1_min_alarm ADIN/ADIN2 Undervoltage alarm +in1_max_alarm ADIN/ADIN2 Overvoltage alarm + +in2_input ADIN voltage (mV) +in2_min_alarm ADIN/ADIN2 Undervoltage alarm +in2_max_alarm ADIN/ADIN2 Overvoltage alarm + +curr1_input SENSE current (mA) +curr1_alarm SENSE overcurrent alarm diff --git a/MAINTAINERS b/MAINTAINERS index 541451050b36..146b8a068a4e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3765,6 +3765,13 @@ L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/sym53c8xx_2/ +LTC4261 HARDWARE MONITOR DRIVER +M: Guenter Roeck +L: lm-sensors@lm-sensors.org +S: Maintained +F: Documentation/hwmon/ltc4261 +F: drivers/hwmon/ltc4261.c + LTP (Linux Test Project) M: Rishikesh K Rajak M: Garrett Cooper diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e382da3122b7..ec4295fd22f6 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -654,6 +654,17 @@ config SENSORS_LTC4245 This driver can also be built as a module. If so, the module will be called ltc4245. +config SENSORS_LTC4261 + tristate "Linear Technology LTC4261" + depends on I2C && EXPERIMENTAL + default n + help + If you say yes here you get support for Linear Technology LTC4261 + Negative Voltage Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4261. + config SENSORS_LM95241 tristate "National Semiconductor LM95241 sensor chip" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index ec9cb735c898..fc38e7c131f8 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -79,6 +79,7 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o +obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c new file mode 100644 index 000000000000..267626178678 --- /dev/null +++ b/drivers/hwmon/ltc4261.c @@ -0,0 +1,315 @@ +/* + * Driver for Linear Technology LTC4261 I2C Negative Voltage Hot Swap Controller + * + * Copyright (C) 2010 Ericsson AB. + * + * Derived from: + * + * Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller + * Copyright (C) 2008 Ira W. Snyder + * + * Datasheet: http://cds.linear.com/docs/Datasheet/42612fb.pdf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* chip registers */ +#define LTC4261_STATUS 0x00 /* readonly */ +#define LTC4261_FAULT 0x01 +#define LTC4261_ALERT 0x02 +#define LTC4261_CONTROL 0x03 +#define LTC4261_SENSE_H 0x04 +#define LTC4261_SENSE_L 0x05 +#define LTC4261_ADIN2_H 0x06 +#define LTC4261_ADIN2_L 0x07 +#define LTC4261_ADIN_H 0x08 +#define LTC4261_ADIN_L 0x09 + +/* + * Fault register bits + */ +#define FAULT_OV (1<<0) +#define FAULT_UV (1<<1) +#define FAULT_OC (1<<2) + +struct ltc4261_data { + struct device *hwmon_dev; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + /* Registers */ + u8 regs[10]; +}; + +static struct ltc4261_data *ltc4261_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ltc4261_data *data = i2c_get_clientdata(client); + struct ltc4261_data *ret = data; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ / 4) || !data->valid) { + int i; + + /* Read registers -- 0x00 to 0x09 */ + for (i = 0; i < ARRAY_SIZE(data->regs); i++) { + int val; + + val = i2c_smbus_read_byte_data(client, i); + if (unlikely(val < 0)) { + dev_dbg(dev, + "Failed to read ADC value: error %d", + val); + ret = ERR_PTR(val); + goto abort; + } + data->regs[i] = val; + } + data->last_updated = jiffies; + data->valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +/* Return the voltage from the given register in mV or mA */ +static int ltc4261_get_value(struct ltc4261_data *data, u8 reg) +{ + u32 val; + + val = (data->regs[reg] << 2) + (data->regs[reg + 1] >> 6); + + switch (reg) { + case LTC4261_ADIN_H: + case LTC4261_ADIN2_H: + /* 2.5mV resolution. Convert to mV. */ + val = val * 25 / 10; + break; + case LTC4261_SENSE_H: + /* + * 62.5uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val = val * 625 / 10; + break; + default: + /* If we get here, the developer messed up */ + WARN_ON_ONCE(1); + val = 0; + break; + } + + return val; +} + +static ssize_t ltc4261_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ltc4261_data *data = ltc4261_update_device(dev); + int value; + + if (IS_ERR(data)) + return PTR_ERR(data); + + value = ltc4261_get_value(data, attr->index); + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +static ssize_t ltc4261_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct ltc4261_data *data = ltc4261_update_device(dev); + u8 fault; + + if (IS_ERR(data)) + return PTR_ERR(data); + + fault = data->regs[LTC4261_FAULT] & attr->index; + if (fault) /* Clear reported faults in chip register */ + i2c_smbus_write_byte_data(client, LTC4261_FAULT, ~fault); + + return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0); +} + +/* + * These macros are used below in constructing device attribute objects + * for use with sysfs_create_group() to make a sysfs device file + * for each register. + */ + +#define LTC4261_VALUE(name, ltc4261_cmd_idx) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4261_show_value, NULL, ltc4261_cmd_idx) + +#define LTC4261_BOOL(name, mask) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4261_show_bool, NULL, (mask)) + +/* + * Input voltages. + */ +LTC4261_VALUE(in1_input, LTC4261_ADIN_H); +LTC4261_VALUE(in2_input, LTC4261_ADIN2_H); + +/* + * Voltage alarms. The chip has only one set of voltage alarm status bits, + * triggered by input voltage alarms. In many designs, those alarms are + * associated with the ADIN2 sensor, due to the proximity of the ADIN2 pin + * to the OV pin. ADIN2 is, however, not available on all chip variants. + * To ensure that the alarm condition is reported to the user, report it + * with both voltage sensors. + */ +LTC4261_BOOL(in1_min_alarm, FAULT_UV); +LTC4261_BOOL(in1_max_alarm, FAULT_OV); +LTC4261_BOOL(in2_min_alarm, FAULT_UV); +LTC4261_BOOL(in2_max_alarm, FAULT_OV); + +/* Currents (via sense resistor) */ +LTC4261_VALUE(curr1_input, LTC4261_SENSE_H); + +/* Overcurrent alarm */ +LTC4261_BOOL(curr1_max_alarm, FAULT_OC); + +static struct attribute *ltc4261_attributes[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min_alarm.dev_attr.attr, + &sensor_dev_attr_in2_max_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + + NULL, +}; + +static const struct attribute_group ltc4261_group = { + .attrs = ltc4261_attributes, +}; + +static int ltc4261_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct ltc4261_data *data; + int ret; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (i2c_smbus_read_byte_data(client, LTC4261_STATUS) < 0) { + dev_err(&client->dev, "Failed to read register %d:%02x:%02x\n", + adapter->id, client->addr, LTC4261_STATUS); + return -ENODEV; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto out_kzalloc; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Clear faults */ + i2c_smbus_write_byte_data(client, LTC4261_FAULT, 0x00); + + /* Register sysfs hooks */ + ret = sysfs_create_group(&client->dev.kobj, <c4261_group); + if (ret) + goto out_sysfs_create_group; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto out_hwmon_device_register; + } + + return 0; + +out_hwmon_device_register: + sysfs_remove_group(&client->dev.kobj, <c4261_group); +out_sysfs_create_group: + kfree(data); +out_kzalloc: + return ret; +} + +static int ltc4261_remove(struct i2c_client *client) +{ + struct ltc4261_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, <c4261_group); + + kfree(data); + + return 0; +} + +static const struct i2c_device_id ltc4261_id[] = { + {"ltc4261", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ltc4261_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver ltc4261_driver = { + .driver = { + .name = "ltc4261", + }, + .probe = ltc4261_probe, + .remove = ltc4261_remove, + .id_table = ltc4261_id, +}; + +static int __init ltc4261_init(void) +{ + return i2c_add_driver(<c4261_driver); +} + +static void __exit ltc4261_exit(void) +{ + i2c_del_driver(<c4261_driver); +} + +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION("LTC4261 driver"); +MODULE_LICENSE("GPL"); + +module_init(ltc4261_init); +module_exit(ltc4261_exit); -- cgit v1.2.3-70-g09d2 From d9d1dc802ffae507956ceb350eb3f4a995734f1e Mon Sep 17 00:00:00 2001 From: Valerie Aurora Date: Mon, 30 Aug 2010 17:23:12 -0400 Subject: Documentation: Fix trivial typo in filesystems/sharedsubtree.txt Documentation: Fix trivial typo in filesystems/sharedsubtree.txt This typo is easy to ignore unless you have spent a great deal of time thinking about how to eliminate duplicate dentries in unions. Signed-off-by: Valerie Aurora Signed-off-by: Al Viro --- Documentation/filesystems/sharedsubtree.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/sharedsubtree.txt b/Documentation/filesystems/sharedsubtree.txt index fc0e39af43c3..4ede421c9687 100644 --- a/Documentation/filesystems/sharedsubtree.txt +++ b/Documentation/filesystems/sharedsubtree.txt @@ -62,10 +62,10 @@ replicas continue to be exactly same. # mount /dev/sd0 /tmp/a #ls /tmp/a - t1 t2 t2 + t1 t2 t3 #ls /mnt/a - t1 t2 t2 + t1 t2 t3 Note that the mount has propagated to the mount at /mnt as well. -- cgit v1.2.3-70-g09d2 From e1455d1bdccbe056ba53479741b1452106ce59aa Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 6 Oct 2010 10:46:53 +0200 Subject: update block_device_operations documentation Updated Documentation/filesystems/Locking to match the code. Signed-off-by: Christoph Hellwig --- Documentation/filesystems/Locking | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 2db4283efa8d..8a817f656f0a 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -349,21 +349,36 @@ call this method upon the IO completion. --------------------------- block_device_operations ----------------------- prototypes: - int (*open) (struct inode *, struct file *); - int (*release) (struct inode *, struct file *); - int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long); + int (*open) (struct block_device *, fmode_t); + int (*release) (struct gendisk *, fmode_t); + int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); + int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); + int (*direct_access) (struct block_device *, sector_t, void **, unsigned long *); int (*media_changed) (struct gendisk *); + void (*unlock_native_capacity) (struct gendisk *); int (*revalidate_disk) (struct gendisk *); + int (*getgeo)(struct block_device *, struct hd_geometry *); + void (*swap_slot_free_notify) (struct block_device *, unsigned long); locking rules: - BKL bd_sem -open: yes yes -release: yes yes -ioctl: yes no + BKL bd_mutex +open: no yes +release: no yes +ioctl: no no +compat_ioctl: no no +direct_access: no no media_changed: no no +unlock_native_capacity: no no revalidate_disk: no no +getgeo: no no +swap_slot_free_notify: no no (see below) + +media_changed, unlock_native_capacity and revalidate_disk are called only from +check_disk_change(). + +swap_slot_free_notify is called with swap_lock and sometimes the page lock +held. -The last two are called only from check_disk_change(). --------------------------- file_operations ------------------------------- prototypes: -- cgit v1.2.3-70-g09d2 From e340c3537239d5b6a2b21d4245c8577d457b0476 Mon Sep 17 00:00:00 2001 From: "Yang, Bo" Date: Tue, 12 Oct 2010 07:21:59 -0600 Subject: [SCSI] megaraid_sas: Version and documentation update Signed-off-by Bo Yang Signed-off-by: James Bottomley --- Documentation/scsi/ChangeLog.megaraid_sas | 47 +++++++++++++++++++++++++++++++ drivers/scsi/megaraid/megaraid_sas.c | 2 +- drivers/scsi/megaraid/megaraid_sas.h | 6 ++-- 3 files changed, 51 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/scsi/ChangeLog.megaraid_sas b/Documentation/scsi/ChangeLog.megaraid_sas index 30023568805e..00301ed9c371 100644 --- a/Documentation/scsi/ChangeLog.megaraid_sas +++ b/Documentation/scsi/ChangeLog.megaraid_sas @@ -1,3 +1,50 @@ +1 Release Date : Thur. May 03, 2010 09:12:45 PST 2009 - + (emaild-id:megaraidlinux@lsi.com) + Bo Yang + +2 Current Version : 00.00.04.31-rc1 +3 Older Version : 00.00.04.17.1-rc1 + +1. Add the Online Controller Reset (OCR) to the Driver. + OCR is the new feature for megaraid_sas driver which + will allow the fw to do the chip reset which will not + affact the OS behavious. + + To add the OCR support, driver need to do: + a). reset the controller chips -- Xscale and Gen2 which + will change the function calls and add the reset function + related to this two chips. + + b). during the reset, driver will store the pending cmds + which not returned by FW to driver's pending queue. Driver + will re-issue those pending cmds again to FW after the OCR + finished. + + c). In driver's timeout routine, driver will report to + OS as reset. Also driver's queue routine will block the + cmds until the OCR finished. + + d). in Driver's ISR routine, if driver get the FW state as + state change, FW in Failure status and FW support online controller + reset (OCR), driver will start to do the controller reset. + + e). In driver's IOCTL routine, the application cmds will wait for the + OCR to finish, then issue the cmds to FW. + + f). Before driver kill adapter, driver will do last chance of + OCR to see if driver can bring back the FW. + +2. Add the support update flag to the driver to tell LSI megaraid_sas + application which driver will support the device update. So application + will not need to do the device update after application add/del the device + from the system. +3. In driver's timeout routine, driver will do three time reset if fw is in + failed state. Driver will kill adapter if can't bring back FW after the + this three times reset. +4. Add the input parameter max_sectors to 1MB support to our GEN2 controller. + customer can use the input paramenter max_sectors to add 1MB support to GEN2 + controller. + 1 Release Date : Thur. Oct 29, 2009 09:12:45 PST 2009 - (emaild-id:megaraidlinux@lsi.com) Bo Yang diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c index a8a2666831d3..eb29d5085131 100644 --- a/drivers/scsi/megaraid/megaraid_sas.c +++ b/drivers/scsi/megaraid/megaraid_sas.c @@ -10,7 +10,7 @@ * 2 of the License, or (at your option) any later version. * * FILE : megaraid_sas.c - * Version : v00.00.04.17.1-rc1 + * Version : v00.00.04.31-rc1 * * Authors: * (email-id : megaraidlinux@lsi.com) diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 54d1a154b448..ad16f5e60046 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -18,9 +18,9 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "00.00.04.17.1-rc1" -#define MEGASAS_RELDATE "Oct. 29, 2009" -#define MEGASAS_EXT_VERSION "Thu. Oct. 29, 11:41:51 PST 2009" +#define MEGASAS_VERSION "00.00.04.31-rc1" +#define MEGASAS_RELDATE "May 3, 2010" +#define MEGASAS_EXT_VERSION "Mon. May 3, 11:41:51 PST 2010" /* * Device IDs -- cgit v1.2.3-70-g09d2 From eb1c86b8b501ad9a073d9d519105979d31fa0ef2 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Tue, 26 Oct 2010 13:27:42 -0400 Subject: NFS: rename nfs.upcall -> nfs.idmap This patch renames the idmapper upcall program from nfs.upcall to nfs.idmap in the NFS documentation. This is because the program has been renamed in the nfs-utils source. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- Documentation/filesystems/nfs/idmapper.txt | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/nfs/idmapper.txt b/Documentation/filesystems/nfs/idmapper.txt index c3852041a21f..b9b4192ea8b5 100644 --- a/Documentation/filesystems/nfs/idmapper.txt +++ b/Documentation/filesystems/nfs/idmapper.txt @@ -6,7 +6,7 @@ Id mapper is used by NFS to translate user and group ids into names, and to translate user and group names into ids. Part of this translation involves performing an upcall to userspace to request the information. Id mapper will user request-key to perform this upcall and cache the result. The program -/usr/sbin/nfs.upcall should be called by request-key, and will perform the +/usr/sbin/nfs.idmap should be called by request-key, and will perform the translation and initialize a key with the resulting information. NFS_USE_NEW_IDMAPPER must be selected when configuring the kernel to use this @@ -20,12 +20,12 @@ direct the upcall. The following line should be added: #OP TYPE DESCRIPTION CALLOUT INFO PROGRAM ARG1 ARG2 ARG3 ... #====== ======= =============== =============== =============================== -create id_resolver * * /usr/sbin/nfs.upcall %k %d 600 +create id_resolver * * /usr/sbin/nfs.idmap %k %d 600 -This will direct all id_resolver requests to the program /usr/sbin/nfs.upcall. +This will direct all id_resolver requests to the program /usr/sbin/nfs.idmap. The last parameter, 600, defines how many seconds into the future the key will -expire. This parameter is optional for /usr/sbin/nfs.upcall. When the timeout -is not specified, nfs.upcall will default to 600 seconds. +expire. This parameter is optional for /usr/sbin/nfs.idmap. When the timeout +is not specified, nfs.idmap will default to 600 seconds. id mapper uses for key descriptions: uid: Find the UID for the given user @@ -39,29 +39,29 @@ would edit your request-key.conf so it look similar to this: #OP TYPE DESCRIPTION CALLOUT INFO PROGRAM ARG1 ARG2 ARG3 ... #====== ======= =============== =============== =============================== -create id_resolver uid:* * /some/other/program %k %d 600 -create id_resolver * * /usr/sbin/nfs.upcall %k %d 600 +create id_resolver uid:* * /some/other/program %k %d 600 +create id_resolver * * /usr/sbin/nfs.idmap %k %d 600 Notice that the new line was added above the line for the generic program. request-key will find the first matching line and corresponding program. In this case, /some/other/program will handle all uid lookups and -/usr/sbin/nfs.upcall will handle gid, user, and group lookups. +/usr/sbin/nfs.idmap will handle gid, user, and group lookups. See for more information about the request-key function. -========== -nfs.upcall -========== -nfs.upcall is designed to be called by request-key, and should not be run "by +========= +nfs.idmap +========= +nfs.idmap is designed to be called by request-key, and should not be run "by hand". This program takes two arguments, a serialized key and a key description. The serialized key is first converted into a key_serial_t, and then passed as an argument to keyctl_instantiate (both are part of keyutils.h). -The actual lookups are performed by functions found in nfsidmap.h. nfs.upcall +The actual lookups are performed by functions found in nfsidmap.h. nfs.idmap determines the correct function to call by looking at the first part of the description string. For example, a uid lookup description will appear as "uid:user@domain". -nfs.upcall will return 0 if the key was instantiated, and non-zero otherwise. +nfs.idmap will return 0 if the key was instantiated, and non-zero otherwise. -- cgit v1.2.3-70-g09d2 From e7f8567db9a7f6b3151b0b275e245c1cef0d9c70 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 26 Oct 2010 15:41:33 -0600 Subject: resources: support allocating space within a region from the top down Allocate space from the top of a region first, then work downward, if an architecture desires this. When we allocate space from a resource, we look for gaps between children of the resource. Previously, we always looked at gaps from the bottom up. For example, given this: [mem 0xbff00000-0xf7ffffff] PCI Bus 0000:00 [mem 0xbff00000-0xbfffffff] gap -- available [mem 0xc0000000-0xdfffffff] PCI Bus 0000:02 [mem 0xe0000000-0xf7ffffff] gap -- available we attempted to allocate from the [mem 0xbff00000-0xbfffffff] gap first, then the [mem 0xe0000000-0xf7ffffff] gap. With this patch an architecture can choose to allocate from the top gap [mem 0xe0000000-0xf7ffffff] first. We can't do this across the board because iomem_resource.end is initialized to 0xffffffff_ffffffff on 64-bit architectures, and most machines can't address the entire 64-bit physical address space. Therefore, we only allocate top-down if the arch requests it by clearing "resource_alloc_from_bottom". Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- Documentation/kernel-parameters.txt | 5 ++ include/linux/ioport.h | 1 + kernel/resource.c | 98 +++++++++++++++++++++++++++++++++++-- 3 files changed, 100 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 8dd7248508a9..fe50cbd315b0 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2156,6 +2156,11 @@ and is between 256 and 4096 characters. It is defined in the file reset_devices [KNL] Force drivers to reset the underlying device during initialization. + resource_alloc_from_bottom + Allocate new resources from the beginning of available + space, not the end. If you need to use this, please + report a bug. + resume= [SWSUSP] Specify the partition device for software suspend diff --git a/include/linux/ioport.h b/include/linux/ioport.h index b22790268b64..d377ea815d45 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -112,6 +112,7 @@ struct resource_list { /* PC/ISA/whatever - the normal PC address spaces: IO and memory */ extern struct resource ioport_resource; extern struct resource iomem_resource; +extern int resource_alloc_from_bottom; extern struct resource *request_resource_conflict(struct resource *root, struct resource *new); extern int request_resource(struct resource *root, struct resource *new); diff --git a/kernel/resource.c b/kernel/resource.c index e15b922d4ba4..716b6804077e 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -40,6 +40,23 @@ EXPORT_SYMBOL(iomem_resource); static DEFINE_RWLOCK(resource_lock); +/* + * By default, we allocate free space bottom-up. The architecture can request + * top-down by clearing this flag. The user can override the architecture's + * choice with the "resource_alloc_from_bottom" kernel boot option, but that + * should only be a debugging tool. + */ +int resource_alloc_from_bottom = 1; + +static __init int setup_alloc_from_bottom(char *s) +{ + printk(KERN_INFO + "resource: allocating from bottom-up; please report a bug\n"); + resource_alloc_from_bottom = 1; + return 0; +} +early_param("resource_alloc_from_bottom", setup_alloc_from_bottom); + static void *r_next(struct seq_file *m, void *v, loff_t *pos) { struct resource *p = v; @@ -379,8 +396,75 @@ static bool resource_contains(struct resource *res1, struct resource *res2) return res1->start <= res2->start && res1->end >= res2->end; } +/* + * Find the resource before "child" in the sibling list of "root" children. + */ +static struct resource *find_sibling_prev(struct resource *root, struct resource *child) +{ + struct resource *this; + + for (this = root->child; this; this = this->sibling) + if (this->sibling == child) + return this; + + return NULL; +} + +/* + * Find empty slot in the resource tree given range and alignment. + * This version allocates from the end of the root resource first. + */ +static int find_resource_from_top(struct resource *root, struct resource *new, + resource_size_t size, resource_size_t min, + resource_size_t max, resource_size_t align, + resource_size_t (*alignf)(void *, + const struct resource *, + resource_size_t, + resource_size_t), + void *alignf_data) +{ + struct resource *this; + struct resource tmp, avail, alloc; + + tmp.start = root->end; + tmp.end = root->end; + + this = find_sibling_prev(root, NULL); + for (;;) { + if (this) { + if (this->end < root->end) + tmp.start = this->end + 1; + } else + tmp.start = root->start; + + resource_clip(&tmp, min, max); + + /* Check for overflow after ALIGN() */ + avail = *new; + avail.start = ALIGN(tmp.start, align); + avail.end = tmp.end; + if (avail.start >= tmp.start) { + alloc.start = alignf(alignf_data, &avail, size, align); + alloc.end = alloc.start + size - 1; + if (resource_contains(&avail, &alloc)) { + new->start = alloc.start; + new->end = alloc.end; + return 0; + } + } + + if (!this || this->start == root->start) + break; + + tmp.end = this->start - 1; + this = find_sibling_prev(root, this); + } + return -EBUSY; +} + /* * Find empty slot in the resource tree given range and alignment. + * This version allocates from the beginning of the root resource first. */ static int find_resource(struct resource *root, struct resource *new, resource_size_t size, resource_size_t min, @@ -396,14 +480,15 @@ static int find_resource(struct resource *root, struct resource *new, tmp.start = root->start; /* - * Skip past an allocated resource that starts at 0, since the assignment - * of this->start - 1 to tmp->end below would cause an underflow. + * Skip past an allocated resource that starts at 0, since the + * assignment of this->start - 1 to tmp->end below would cause an + * underflow. */ if (this && this->start == 0) { tmp.start = this->end + 1; this = this->sibling; } - for(;;) { + for (;;) { if (this) tmp.end = this->start - 1; else @@ -424,8 +509,10 @@ static int find_resource(struct resource *root, struct resource *new, return 0; } } + if (!this) break; + tmp.start = this->end + 1; this = this->sibling; } @@ -458,7 +545,10 @@ int allocate_resource(struct resource *root, struct resource *new, alignf = simple_align_resource; write_lock(&resource_lock); - err = find_resource(root, new, size, min, max, align, alignf, alignf_data); + if (resource_alloc_from_bottom) + err = find_resource(root, new, size, min, max, align, alignf, alignf_data); + else + err = find_resource_from_top(root, new, size, min, max, align, alignf, alignf_data); if (err >= 0 && __request_resource(root, new)) err = -EBUSY; write_unlock(&resource_lock); -- cgit v1.2.3-70-g09d2 From 0f4d208f1975f16f269134cee5f44c1f048581da Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Tue, 26 Oct 2010 14:21:22 -0700 Subject: Documentation/filesystems/proc.txt: improve smaps field documentation Signed-off-by: Matt Mackall Cc: Nikanth Karthikesan Cc: Balbir Singh Cc: Wu Fengguang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/proc.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index a6aca8740883..a563b74c7aef 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -374,13 +374,13 @@ Swap: 0 kB KernelPageSize: 4 kB MMUPageSize: 4 kB -The first of these lines shows the same information as is displayed for the -mapping in /proc/PID/maps. The remaining lines show the size of the mapping, -the amount of the mapping that is currently resident in RAM, the "proportional -set size” (divide each shared page by the number of processes sharing it), the -number of clean and dirty shared pages in the mapping, and the number of clean -and dirty private pages in the mapping. The "Referenced" indicates the amount -of memory currently marked as referenced or accessed. +The first of these lines shows the same information as is displayed for the +mapping in /proc/PID/maps. The remaining lines show the size of the mapping +(size), the amount of the mapping that is currently resident in RAM (RSS), the +process' proportional share of this mapping (PSS), the number of clean and +dirty shared pages in the mapping, and the number of clean and dirty private +pages in the mapping. The "Referenced" indicates the amount of memory +currently marked as referenced or accessed. This file is only present if the CONFIG_MMU kernel configuration option is enabled. -- cgit v1.2.3-70-g09d2 From e11da5b4fdf01d71d73c21cb92b00595b917d7fd Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 26 Oct 2010 14:21:40 -0700 Subject: tracing, vmscan: add trace events for LRU list shrinking There have been numerous reports of stalls that pointed at the problem being somewhere in the VM. There are multiple roots to the problems which means dealing with any of the root problems in isolation is tricky to justify on their own and they would still need integration testing. This patch series puts together two different patch sets which in combination should tackle some of the root causes of latency problems being reported. Patch 1 adds a tracepoint for shrink_inactive_list. For this series, the most important results is being able to calculate the scanning/reclaim ratio as a measure of the amount of work being done by page reclaim. Patch 2 accounts for time spent in congestion_wait. Patches 3-6 were originally developed by Kosaki Motohiro but reworked for this series. It has been noted that lumpy reclaim is far too aggressive and trashes the system somewhat. As SLUB uses high-order allocations, a large cost incurred by lumpy reclaim will be noticeable. It was also reported during transparent hugepage support testing that lumpy reclaim was trashing the system and these patches should mitigate that problem without disabling lumpy reclaim. Patch 7 adds wait_iff_congested() and replaces some callers of congestion_wait(). wait_iff_congested() only sleeps if there is a BDI that is currently congested. Patch 8 notes that any BDI being congested is not necessarily a problem because there could be multiple BDIs of varying speeds and numberous zones. It attempts to track when a zone being reclaimed contains many pages backed by a congested BDI and if so, reclaimers wait on the congestion queue. I ran a number of tests with monitoring on X86, X86-64 and PPC64. Each machine had 3G of RAM and the CPUs were X86: Intel P4 2-core X86-64: AMD Phenom 4-core PPC64: PPC970MP Each used a single disk and the onboard IO controller. Dirty ratio was left at 20. I'm just going to report for X86-64 and PPC64 in a vague attempt to keep this report short. Four kernels were tested each based on v2.6.36-rc4 traceonly-v2r2: Patches 1 and 2 to instrument vmscan reclaims and congestion_wait lowlumpy-v2r3: Patches 1-6 to test if lumpy reclaim is better waitcongest-v2r3: Patches 1-7 to only wait on congestion waitwriteback-v2r4: Patches 1-8 to detect when a zone is congested nocongest-v1r5: Patches 1-3 for testing wait_iff_congestion nodirect-v1r5: Patches 1-10 to disable filesystem writeback for better IO The tests run were as follows kernbench compile-based benchmark. Smoke test performance sysbench OLTP read-only benchmark. Will be re-run in the future as read-write micro-mapped-file-stream This is a micro-benchmark from Johannes Weiner that accesses a large sparse-file through mmap(). It was configured to run in only single-CPU mode but can be indicative of how well page reclaim identifies suitable pages. stress-highalloc Tries to allocate huge pages under heavy load. kernbench, iozone and sysbench did not report any performance regression on any machine. sysbench did pressure the system lightly and there was reclaim activity but there were no difference of major interest between the kernels. X86-64 micro-mapped-file-stream traceonly-v2r2 lowlumpy-v2r3 waitcongest-v2r3 waitwriteback-v2r4 pgalloc_dma 1639.00 ( 0.00%) 667.00 (-145.73%) 1167.00 ( -40.45%) 578.00 (-183.56%) pgalloc_dma32 2842410.00 ( 0.00%) 2842626.00 ( 0.01%) 2843043.00 ( 0.02%) 2843014.00 ( 0.02%) pgalloc_normal 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) pgsteal_dma 729.00 ( 0.00%) 85.00 (-757.65%) 609.00 ( -19.70%) 125.00 (-483.20%) pgsteal_dma32 2338721.00 ( 0.00%) 2447354.00 ( 4.44%) 2429536.00 ( 3.74%) 2436772.00 ( 4.02%) pgsteal_normal 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) pgscan_kswapd_dma 1469.00 ( 0.00%) 532.00 (-176.13%) 1078.00 ( -36.27%) 220.00 (-567.73%) pgscan_kswapd_dma32 4597713.00 ( 0.00%) 4503597.00 ( -2.09%) 4295673.00 ( -7.03%) 3891686.00 ( -18.14%) pgscan_kswapd_normal 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) pgscan_direct_dma 71.00 ( 0.00%) 134.00 ( 47.01%) 243.00 ( 70.78%) 352.00 ( 79.83%) pgscan_direct_dma32 305820.00 ( 0.00%) 280204.00 ( -9.14%) 600518.00 ( 49.07%) 957485.00 ( 68.06%) pgscan_direct_normal 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) pageoutrun 16296.00 ( 0.00%) 21254.00 ( 23.33%) 18447.00 ( 11.66%) 20067.00 ( 18.79%) allocstall 443.00 ( 0.00%) 273.00 ( -62.27%) 513.00 ( 13.65%) 1568.00 ( 71.75%) These are based on the raw figures taken from /proc/vmstat. It's a rough measure of reclaim activity. Note that allocstall counts are higher because we are entering direct reclaim more often as a result of not sleeping in congestion. In itself, it's not necessarily a bad thing. It's easier to get a view of what happened from the vmscan tracepoint report. FTrace Reclaim Statistics: vmscan traceonly-v2r2 lowlumpy-v2r3 waitcongest-v2r3 waitwriteback-v2r4 Direct reclaims 443 273 513 1568 Direct reclaim pages scanned 305968 280402 600825 957933 Direct reclaim pages reclaimed 43503 19005 30327 117191 Direct reclaim write file async I/O 0 0 0 0 Direct reclaim write anon async I/O 0 3 4 12 Direct reclaim write file sync I/O 0 0 0 0 Direct reclaim write anon sync I/O 0 0 0 0 Wake kswapd requests 187649 132338 191695 267701 Kswapd wakeups 3 1 4 1 Kswapd pages scanned 4599269 4454162 4296815 3891906 Kswapd pages reclaimed 2295947 2428434 2399818 2319706 Kswapd reclaim write file async I/O 1 0 1 1 Kswapd reclaim write anon async I/O 59 187 41 222 Kswapd reclaim write file sync I/O 0 0 0 0 Kswapd reclaim write anon sync I/O 0 0 0 0 Time stalled direct reclaim (seconds) 4.34 2.52 6.63 2.96 Time kswapd awake (seconds) 11.15 10.25 11.01 10.19 Total pages scanned 4905237 4734564 4897640 4849839 Total pages reclaimed 2339450 2447439 2430145 2436897 %age total pages scanned/reclaimed 47.69% 51.69% 49.62% 50.25% %age total pages scanned/written 0.00% 0.00% 0.00% 0.00% %age file pages scanned/written 0.00% 0.00% 0.00% 0.00% Percentage Time Spent Direct Reclaim 29.23% 19.02% 38.48% 20.25% Percentage Time kswapd Awake 78.58% 78.85% 76.83% 79.86% What is interesting here for nocongest in particular is that while direct reclaim scans more pages, the overall number of pages scanned remains the same and the ratio of pages scanned to pages reclaimed is more or less the same. In other words, while we are sleeping less, reclaim is not doing more work and as direct reclaim and kswapd is awake for less time, it would appear to be doing less work. FTrace Reclaim Statistics: congestion_wait Direct number congest waited 87 196 64 0 Direct time congest waited 4604ms 4732ms 5420ms 0ms Direct full congest waited 72 145 53 0 Direct number conditional waited 0 0 324 1315 Direct time conditional waited 0ms 0ms 0ms 0ms Direct full conditional waited 0 0 0 0 KSwapd number congest waited 20 10 15 7 KSwapd time congest waited 1264ms 536ms 884ms 284ms KSwapd full congest waited 10 4 6 2 KSwapd number conditional waited 0 0 0 0 KSwapd time conditional waited 0ms 0ms 0ms 0ms KSwapd full conditional waited 0 0 0 0 The vanilla kernel spent 8 seconds asleep in direct reclaim and no time at all asleep with the patches. MMTests Statistics: duration User/Sys Time Running Test (seconds) 10.51 10.73 10.6 11.66 Total Elapsed Time (seconds) 14.19 13.00 14.33 12.76 Overall, the tests completed faster. It is interesting to note that backing off further when a zone is congested and not just a BDI was more efficient overall. PPC64 micro-mapped-file-stream pgalloc_dma 3024660.00 ( 0.00%) 3027185.00 ( 0.08%) 3025845.00 ( 0.04%) 3026281.00 ( 0.05%) pgalloc_normal 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) pgsteal_dma 2508073.00 ( 0.00%) 2565351.00 ( 2.23%) 2463577.00 ( -1.81%) 2532263.00 ( 0.96%) pgsteal_normal 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) pgscan_kswapd_dma 4601307.00 ( 0.00%) 4128076.00 ( -11.46%) 3912317.00 ( -17.61%) 3377165.00 ( -36.25%) pgscan_kswapd_normal 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) pgscan_direct_dma 629825.00 ( 0.00%) 971622.00 ( 35.18%) 1063938.00 ( 40.80%) 1711935.00 ( 63.21%) pgscan_direct_normal 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) pageoutrun 27776.00 ( 0.00%) 20458.00 ( -35.77%) 18763.00 ( -48.04%) 18157.00 ( -52.98%) allocstall 977.00 ( 0.00%) 2751.00 ( 64.49%) 2098.00 ( 53.43%) 5136.00 ( 80.98%) Similar trends to x86-64. allocstalls are up but it's not necessarily bad. FTrace Reclaim Statistics: vmscan Direct reclaims 977 2709 2098 5136 Direct reclaim pages scanned 629825 963814 1063938 1711935 Direct reclaim pages reclaimed 75550 242538 150904 387647 Direct reclaim write file async I/O 0 0 0 2 Direct reclaim write anon async I/O 0 10 0 4 Direct reclaim write file sync I/O 0 0 0 0 Direct reclaim write anon sync I/O 0 0 0 0 Wake kswapd requests 392119 1201712 571935 571921 Kswapd wakeups 3 2 3 3 Kswapd pages scanned 4601307 4128076 3912317 3377165 Kswapd pages reclaimed 2432523 2318797 2312673 2144616 Kswapd reclaim write file async I/O 20 1 1 1 Kswapd reclaim write anon async I/O 57 132 11 121 Kswapd reclaim write file sync I/O 0 0 0 0 Kswapd reclaim write anon sync I/O 0 0 0 0 Time stalled direct reclaim (seconds) 6.19 7.30 13.04 10.88 Time kswapd awake (seconds) 21.73 26.51 25.55 23.90 Total pages scanned 5231132 5091890 4976255 5089100 Total pages reclaimed 2508073 2561335 2463577 2532263 %age total pages scanned/reclaimed 47.95% 50.30% 49.51% 49.76% %age total pages scanned/written 0.00% 0.00% 0.00% 0.00% %age file pages scanned/written 0.00% 0.00% 0.00% 0.00% Percentage Time Spent Direct Reclaim 18.89% 20.65% 32.65% 27.65% Percentage Time kswapd Awake 72.39% 80.68% 78.21% 77.40% Again, a similar trend that the congestion_wait changes mean that direct reclaim scans more pages but the overall number of pages scanned while slightly reduced, are very similar. The ratio of scanning/reclaimed remains roughly similar. The downside is that kswapd and direct reclaim was awake longer and for a larger percentage of the overall workload. It's possible there were big differences in the amount of time spent reclaiming slab pages between the different kernels which is plausible considering that the micro tests runs after fsmark and sysbench. Trace Reclaim Statistics: congestion_wait Direct number congest waited 845 1312 104 0 Direct time congest waited 19416ms 26560ms 7544ms 0ms Direct full congest waited 745 1105 72 0 Direct number conditional waited 0 0 1322 2935 Direct time conditional waited 0ms 0ms 12ms 312ms Direct full conditional waited 0 0 0 3 KSwapd number congest waited 39 102 75 63 KSwapd time congest waited 2484ms 6760ms 5756ms 3716ms KSwapd full congest waited 20 48 46 25 KSwapd number conditional waited 0 0 0 0 KSwapd time conditional waited 0ms 0ms 0ms 0ms KSwapd full conditional waited 0 0 0 0 The vanilla kernel spent 20 seconds asleep in direct reclaim and only 312ms asleep with the patches. The time kswapd spent congest waited was also reduced by a large factor. MMTests Statistics: duration ser/Sys Time Running Test (seconds) 26.58 28.05 26.9 28.47 Total Elapsed Time (seconds) 30.02 32.86 32.67 30.88 With all patches applies, the completion times are very similar. X86-64 STRESS-HIGHALLOC traceonly-v2r2 lowlumpy-v2r3 waitcongest-v2r3waitwriteback-v2r4 Pass 1 82.00 ( 0.00%) 84.00 ( 2.00%) 85.00 ( 3.00%) 85.00 ( 3.00%) Pass 2 90.00 ( 0.00%) 87.00 (-3.00%) 88.00 (-2.00%) 89.00 (-1.00%) At Rest 92.00 ( 0.00%) 90.00 (-2.00%) 90.00 (-2.00%) 91.00 (-1.00%) Success figures across the board are broadly similar. traceonly-v2r2 lowlumpy-v2r3 waitcongest-v2r3waitwriteback-v2r4 Direct reclaims 1045 944 886 887 Direct reclaim pages scanned 135091 119604 109382 101019 Direct reclaim pages reclaimed 88599 47535 47863 46671 Direct reclaim write file async I/O 494 283 465 280 Direct reclaim write anon async I/O 29357 13710 16656 13462 Direct reclaim write file sync I/O 154 2 2 3 Direct reclaim write anon sync I/O 14594 571 509 561 Wake kswapd requests 7491 933 872 892 Kswapd wakeups 814 778 731 780 Kswapd pages scanned 7290822 15341158 11916436 13703442 Kswapd pages reclaimed 3587336 3142496 3094392 3187151 Kswapd reclaim write file async I/O 91975 32317 28022 29628 Kswapd reclaim write anon async I/O 1992022 789307 829745 849769 Kswapd reclaim write file sync I/O 0 0 0 0 Kswapd reclaim write anon sync I/O 0 0 0 0 Time stalled direct reclaim (seconds) 4588.93 2467.16 2495.41 2547.07 Time kswapd awake (seconds) 2497.66 1020.16 1098.06 1176.82 Total pages scanned 7425913 15460762 12025818 13804461 Total pages reclaimed 3675935 3190031 3142255 3233822 %age total pages scanned/reclaimed 49.50% 20.63% 26.13% 23.43% %age total pages scanned/written 28.66% 5.41% 7.28% 6.47% %age file pages scanned/written 1.25% 0.21% 0.24% 0.22% Percentage Time Spent Direct Reclaim 57.33% 42.15% 42.41% 42.99% Percentage Time kswapd Awake 43.56% 27.87% 29.76% 31.25% Scanned/reclaimed ratios again look good with big improvements in efficiency. The Scanned/written ratios also look much improved. With a better scanned/written ration, there is an expectation that IO would be more efficient and indeed, the time spent in direct reclaim is much reduced by the full series and kswapd spends a little less time awake. Overall, indications here are that allocations were happening much faster and this can be seen with a graph of the latency figures as the allocations were taking place http://www.csn.ul.ie/~mel/postings/vmscanreduce-20101509/highalloc-interlatency-hydra-mean.ps FTrace Reclaim Statistics: congestion_wait Direct number congest waited 1333 204 169 4 Direct time congest waited 78896ms 8288ms 7260ms 200ms Direct full congest waited 756 92 69 2 Direct number conditional waited 0 0 26 186 Direct time conditional waited 0ms 0ms 0ms 2504ms Direct full conditional waited 0 0 0 25 KSwapd number congest waited 4 395 227 282 KSwapd time congest waited 384ms 25136ms 10508ms 18380ms KSwapd full congest waited 3 232 98 176 KSwapd number conditional waited 0 0 0 0 KSwapd time conditional waited 0ms 0ms 0ms 0ms KSwapd full conditional waited 0 0 0 0 KSwapd full conditional waited 318 0 312 9 Overall, the time spent speeping is reduced. kswapd is still hitting congestion_wait() but that is because there are callers remaining where it wasn't clear in advance if they should be changed to wait_iff_congested() or not. Overall the sleep imes are reduced though - from 79ish seconds to about 19. MMTests Statistics: duration User/Sys Time Running Test (seconds) 3415.43 3386.65 3388.39 3377.5 Total Elapsed Time (seconds) 5733.48 3660.33 3689.41 3765.39 With the full series, the time to complete the tests are reduced by 30% PPC64 STRESS-HIGHALLOC traceonly-v2r2 lowlumpy-v2r3 waitcongest-v2r3waitwriteback-v2r4 Pass 1 17.00 ( 0.00%) 34.00 (17.00%) 38.00 (21.00%) 43.00 (26.00%) Pass 2 25.00 ( 0.00%) 37.00 (12.00%) 42.00 (17.00%) 46.00 (21.00%) At Rest 49.00 ( 0.00%) 43.00 (-6.00%) 45.00 (-4.00%) 51.00 ( 2.00%) Success rates there are *way* up particularly considering that the 16MB huge pages on PPC64 mean that it's always much harder to allocate them. FTrace Reclaim Statistics: vmscan stress-highalloc stress-highalloc stress-highalloc stress-highalloc traceonly-v2r2 lowlumpy-v2r3 waitcongest-v2r3waitwriteback-v2r4 Direct reclaims 499 505 564 509 Direct reclaim pages scanned 223478 41898 51818 45605 Direct reclaim pages reclaimed 137730 21148 27161 23455 Direct reclaim write file async I/O 399 136 162 136 Direct reclaim write anon async I/O 46977 2865 4686 3998 Direct reclaim write file sync I/O 29 0 1 3 Direct reclaim write anon sync I/O 31023 159 237 239 Wake kswapd requests 420 351 360 326 Kswapd wakeups 185 294 249 277 Kswapd pages scanned 15703488 16392500 17821724 17598737 Kswapd pages reclaimed 5808466 2908858 3139386 3145435 Kswapd reclaim write file async I/O 159938 18400 18717 13473 Kswapd reclaim write anon async I/O 3467554 228957 322799 234278 Kswapd reclaim write file sync I/O 0 0 0 0 Kswapd reclaim write anon sync I/O 0 0 0 0 Time stalled direct reclaim (seconds) 9665.35 1707.81 2374.32 1871.23 Time kswapd awake (seconds) 9401.21 1367.86 1951.75 1328.88 Total pages scanned 15926966 16434398 17873542 17644342 Total pages reclaimed 5946196 2930006 3166547 3168890 %age total pages scanned/reclaimed 37.33% 17.83% 17.72% 17.96% %age total pages scanned/written 23.27% 1.52% 1.94% 1.43% %age file pages scanned/written 1.01% 0.11% 0.11% 0.08% Percentage Time Spent Direct Reclaim 44.55% 35.10% 41.42% 36.91% Percentage Time kswapd Awake 86.71% 43.58% 52.67% 41.14% While the scanning rates are slightly up, the scanned/reclaimed and scanned/written figures are much improved. The time spent in direct reclaim and with kswapd are massively reduced, mostly by the lowlumpy patches. FTrace Reclaim Statistics: congestion_wait Direct number congest waited 725 303 126 3 Direct time congest waited 45524ms 9180ms 5936ms 300ms Direct full congest waited 487 190 52 3 Direct number conditional waited 0 0 200 301 Direct time conditional waited 0ms 0ms 0ms 1904ms Direct full conditional waited 0 0 0 19 KSwapd number congest waited 0 2 23 4 KSwapd time congest waited 0ms 200ms 420ms 404ms KSwapd full congest waited 0 2 2 4 KSwapd number conditional waited 0 0 0 0 KSwapd time conditional waited 0ms 0ms 0ms 0ms KSwapd full conditional waited 0 0 0 0 Not as dramatic a story here but the time spent asleep is reduced and we can still see what wait_iff_congested is going to sleep when necessary. MMTests Statistics: duration User/Sys Time Running Test (seconds) 12028.09 3157.17 3357.79 3199.16 Total Elapsed Time (seconds) 10842.07 3138.72 3705.54 3229.85 The time to complete this test goes way down. With the full series, we are allocating over twice the number of huge pages in 30% of the time and there is a corresponding impact on the allocation latency graph available at. http://www.csn.ul.ie/~mel/postings/vmscanreduce-20101509/highalloc-interlatency-powyah-mean.ps This patch: Add a trace event for shrink_inactive_list() and updates the sample postprocessing script appropriately. It can be used to determine how many pages were reclaimed and for non-lumpy reclaim where exactly the pages were reclaimed from. Signed-off-by: Mel Gorman Cc: Johannes Weiner Cc: Minchan Kim Cc: Wu Fengguang Cc: KAMEZAWA Hiroyuki Cc: KOSAKI Motohiro Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- .../trace/postprocess/trace-vmscan-postprocess.pl | 39 ++++++++++++++------ include/trace/events/vmscan.h | 42 ++++++++++++++++++++++ mm/vmscan.c | 6 ++++ 3 files changed, 77 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/trace/postprocess/trace-vmscan-postprocess.pl b/Documentation/trace/postprocess/trace-vmscan-postprocess.pl index 1b55146d1c8d..b3e73ddb1567 100644 --- a/Documentation/trace/postprocess/trace-vmscan-postprocess.pl +++ b/Documentation/trace/postprocess/trace-vmscan-postprocess.pl @@ -46,7 +46,7 @@ use constant HIGH_KSWAPD_LATENCY => 20; use constant HIGH_KSWAPD_REWAKEUP => 21; use constant HIGH_NR_SCANNED => 22; use constant HIGH_NR_TAKEN => 23; -use constant HIGH_NR_RECLAIM => 24; +use constant HIGH_NR_RECLAIMED => 24; use constant HIGH_NR_CONTIG_DIRTY => 25; my %perprocesspid; @@ -58,11 +58,13 @@ my $opt_read_procstat; my $total_wakeup_kswapd; my ($total_direct_reclaim, $total_direct_nr_scanned); my ($total_direct_latency, $total_kswapd_latency); +my ($total_direct_nr_reclaimed); my ($total_direct_writepage_file_sync, $total_direct_writepage_file_async); my ($total_direct_writepage_anon_sync, $total_direct_writepage_anon_async); my ($total_kswapd_nr_scanned, $total_kswapd_wake); my ($total_kswapd_writepage_file_sync, $total_kswapd_writepage_file_async); my ($total_kswapd_writepage_anon_sync, $total_kswapd_writepage_anon_async); +my ($total_kswapd_nr_reclaimed); # Catch sigint and exit on request my $sigint_report = 0; @@ -104,7 +106,7 @@ my $regex_kswapd_wake_default = 'nid=([0-9]*) order=([0-9]*)'; my $regex_kswapd_sleep_default = 'nid=([0-9]*)'; my $regex_wakeup_kswapd_default = 'nid=([0-9]*) zid=([0-9]*) order=([0-9]*)'; my $regex_lru_isolate_default = 'isolate_mode=([0-9]*) order=([0-9]*) nr_requested=([0-9]*) nr_scanned=([0-9]*) nr_taken=([0-9]*) contig_taken=([0-9]*) contig_dirty=([0-9]*) contig_failed=([0-9]*)'; -my $regex_lru_shrink_inactive_default = 'lru=([A-Z_]*) nr_scanned=([0-9]*) nr_reclaimed=([0-9]*) priority=([0-9]*)'; +my $regex_lru_shrink_inactive_default = 'nid=([0-9]*) zid=([0-9]*) nr_scanned=([0-9]*) nr_reclaimed=([0-9]*) priority=([0-9]*) flags=([A-Z_|]*)'; my $regex_lru_shrink_active_default = 'lru=([A-Z_]*) nr_scanned=([0-9]*) nr_rotated=([0-9]*) priority=([0-9]*)'; my $regex_writepage_default = 'page=([0-9a-f]*) pfn=([0-9]*) flags=([A-Z_|]*)'; @@ -203,8 +205,8 @@ $regex_lru_shrink_inactive = generate_traceevent_regex( "vmscan/mm_vmscan_lru_shrink_inactive", $regex_lru_shrink_inactive_default, "nid", "zid", - "lru", - "nr_scanned", "nr_reclaimed", "priority"); + "nr_scanned", "nr_reclaimed", "priority", + "flags"); $regex_lru_shrink_active = generate_traceevent_regex( "vmscan/mm_vmscan_lru_shrink_active", $regex_lru_shrink_active_default, @@ -375,6 +377,16 @@ EVENT_PROCESS: my $nr_contig_dirty = $7; $perprocesspid{$process_pid}->{HIGH_NR_SCANNED} += $nr_scanned; $perprocesspid{$process_pid}->{HIGH_NR_CONTIG_DIRTY} += $nr_contig_dirty; + } elsif ($tracepoint eq "mm_vmscan_lru_shrink_inactive") { + $details = $5; + if ($details !~ /$regex_lru_shrink_inactive/o) { + print "WARNING: Failed to parse mm_vmscan_lru_shrink_inactive as expected\n"; + print " $details\n"; + print " $regex_lru_shrink_inactive/o\n"; + next; + } + my $nr_reclaimed = $4; + $perprocesspid{$process_pid}->{HIGH_NR_RECLAIMED} += $nr_reclaimed; } elsif ($tracepoint eq "mm_vmscan_writepage") { $details = $5; if ($details !~ /$regex_writepage/o) { @@ -464,8 +476,8 @@ sub dump_stats { # Print out process activity printf("\n"); - printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s %8s\n", "Process", "Direct", "Wokeup", "Pages", "Pages", "Pages", "Time"); - printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s %8s\n", "details", "Rclms", "Kswapd", "Scanned", "Sync-IO", "ASync-IO", "Stalled"); + printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s %8s %8s\n", "Process", "Direct", "Wokeup", "Pages", "Pages", "Pages", "Pages", "Time"); + printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s %8s %8s\n", "details", "Rclms", "Kswapd", "Scanned", "Rclmed", "Sync-IO", "ASync-IO", "Stalled"); foreach $process_pid (keys %stats) { if (!$stats{$process_pid}->{MM_VMSCAN_DIRECT_RECLAIM_BEGIN}) { @@ -475,6 +487,7 @@ sub dump_stats { $total_direct_reclaim += $stats{$process_pid}->{MM_VMSCAN_DIRECT_RECLAIM_BEGIN}; $total_wakeup_kswapd += $stats{$process_pid}->{MM_VMSCAN_WAKEUP_KSWAPD}; $total_direct_nr_scanned += $stats{$process_pid}->{HIGH_NR_SCANNED}; + $total_direct_nr_reclaimed += $stats{$process_pid}->{HIGH_NR_RECLAIMED}; $total_direct_writepage_file_sync += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_SYNC}; $total_direct_writepage_anon_sync += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_SYNC}; $total_direct_writepage_file_async += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_ASYNC}; @@ -489,11 +502,12 @@ sub dump_stats { $index++; } - printf("%-" . $max_strlen . "s %8d %10d %8u %8u %8u %8.3f", + printf("%-" . $max_strlen . "s %8d %10d %8u %8u %8u %8u %8.3f", $process_pid, $stats{$process_pid}->{MM_VMSCAN_DIRECT_RECLAIM_BEGIN}, $stats{$process_pid}->{MM_VMSCAN_WAKEUP_KSWAPD}, $stats{$process_pid}->{HIGH_NR_SCANNED}, + $stats{$process_pid}->{HIGH_NR_RECLAIMED}, $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_SYNC} + $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_SYNC}, $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_ASYNC} + $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_ASYNC}, $this_reclaim_delay / 1000); @@ -529,8 +543,8 @@ sub dump_stats { # Print out kswapd activity printf("\n"); - printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s\n", "Kswapd", "Kswapd", "Order", "Pages", "Pages", "Pages"); - printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s\n", "Instance", "Wakeups", "Re-wakeup", "Scanned", "Sync-IO", "ASync-IO"); + printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s\n", "Kswapd", "Kswapd", "Order", "Pages", "Pages", "Pages", "Pages"); + printf("%-" . $max_strlen . "s %8s %10s %8s %8s %8s %8s\n", "Instance", "Wakeups", "Re-wakeup", "Scanned", "Rclmed", "Sync-IO", "ASync-IO"); foreach $process_pid (keys %stats) { if (!$stats{$process_pid}->{MM_VMSCAN_KSWAPD_WAKE}) { @@ -539,16 +553,18 @@ sub dump_stats { $total_kswapd_wake += $stats{$process_pid}->{MM_VMSCAN_KSWAPD_WAKE}; $total_kswapd_nr_scanned += $stats{$process_pid}->{HIGH_NR_SCANNED}; + $total_kswapd_nr_reclaimed += $stats{$process_pid}->{HIGH_NR_RECLAIMED}; $total_kswapd_writepage_file_sync += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_SYNC}; $total_kswapd_writepage_anon_sync += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_SYNC}; $total_kswapd_writepage_file_async += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_ASYNC}; $total_kswapd_writepage_anon_async += $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_ASYNC}; - printf("%-" . $max_strlen . "s %8d %10d %8u %8i %8u", + printf("%-" . $max_strlen . "s %8d %10d %8u %8u %8i %8u", $process_pid, $stats{$process_pid}->{MM_VMSCAN_KSWAPD_WAKE}, $stats{$process_pid}->{HIGH_KSWAPD_REWAKEUP}, $stats{$process_pid}->{HIGH_NR_SCANNED}, + $stats{$process_pid}->{HIGH_NR_RECLAIMED}, $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_SYNC} + $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_SYNC}, $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_ASYNC} + $stats{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_ASYNC}); @@ -579,6 +595,7 @@ sub dump_stats { print "\nSummary\n"; print "Direct reclaims: $total_direct_reclaim\n"; print "Direct reclaim pages scanned: $total_direct_nr_scanned\n"; + print "Direct reclaim pages reclaimed: $total_direct_nr_reclaimed\n"; print "Direct reclaim write file sync I/O: $total_direct_writepage_file_sync\n"; print "Direct reclaim write anon sync I/O: $total_direct_writepage_anon_sync\n"; print "Direct reclaim write file async I/O: $total_direct_writepage_file_async\n"; @@ -588,6 +605,7 @@ sub dump_stats { print "\n"; print "Kswapd wakeups: $total_kswapd_wake\n"; print "Kswapd pages scanned: $total_kswapd_nr_scanned\n"; + print "Kswapd pages reclaimed: $total_kswapd_nr_reclaimed\n"; print "Kswapd reclaim write file sync I/O: $total_kswapd_writepage_file_sync\n"; print "Kswapd reclaim write anon sync I/O: $total_kswapd_writepage_anon_sync\n"; print "Kswapd reclaim write file async I/O: $total_kswapd_writepage_file_async\n"; @@ -612,6 +630,7 @@ sub aggregate_perprocesspid() { $perprocess{$process}->{MM_VMSCAN_WAKEUP_KSWAPD} += $perprocesspid{$process_pid}->{MM_VMSCAN_WAKEUP_KSWAPD}; $perprocess{$process}->{HIGH_KSWAPD_REWAKEUP} += $perprocesspid{$process_pid}->{HIGH_KSWAPD_REWAKEUP}; $perprocess{$process}->{HIGH_NR_SCANNED} += $perprocesspid{$process_pid}->{HIGH_NR_SCANNED}; + $perprocess{$process}->{HIGH_NR_RECLAIMED} += $perprocesspid{$process_pid}->{HIGH_NR_RECLAIMED}; $perprocess{$process}->{MM_VMSCAN_WRITEPAGE_FILE_SYNC} += $perprocesspid{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_SYNC}; $perprocess{$process}->{MM_VMSCAN_WRITEPAGE_ANON_SYNC} += $perprocesspid{$process_pid}->{MM_VMSCAN_WRITEPAGE_ANON_SYNC}; $perprocess{$process}->{MM_VMSCAN_WRITEPAGE_FILE_ASYNC} += $perprocesspid{$process_pid}->{MM_VMSCAN_WRITEPAGE_FILE_ASYNC}; diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h index 370aa5a87322..ecf952192a93 100644 --- a/include/trace/events/vmscan.h +++ b/include/trace/events/vmscan.h @@ -10,6 +10,7 @@ #define RECLAIM_WB_ANON 0x0001u #define RECLAIM_WB_FILE 0x0002u +#define RECLAIM_WB_MIXED 0x0010u #define RECLAIM_WB_SYNC 0x0004u #define RECLAIM_WB_ASYNC 0x0008u @@ -17,6 +18,7 @@ (flags) ? __print_flags(flags, "|", \ {RECLAIM_WB_ANON, "RECLAIM_WB_ANON"}, \ {RECLAIM_WB_FILE, "RECLAIM_WB_FILE"}, \ + {RECLAIM_WB_MIXED, "RECLAIM_WB_MIXED"}, \ {RECLAIM_WB_SYNC, "RECLAIM_WB_SYNC"}, \ {RECLAIM_WB_ASYNC, "RECLAIM_WB_ASYNC"} \ ) : "RECLAIM_WB_NONE" @@ -26,6 +28,12 @@ (sync == PAGEOUT_IO_SYNC ? RECLAIM_WB_SYNC : RECLAIM_WB_ASYNC) \ ) +#define trace_shrink_flags(file, sync) ( \ + (sync == PAGEOUT_IO_SYNC ? RECLAIM_WB_MIXED : \ + (file ? RECLAIM_WB_FILE : RECLAIM_WB_ANON)) | \ + (sync == PAGEOUT_IO_SYNC ? RECLAIM_WB_SYNC : RECLAIM_WB_ASYNC) \ + ) + TRACE_EVENT(mm_vmscan_kswapd_sleep, TP_PROTO(int nid), @@ -269,6 +277,40 @@ TRACE_EVENT(mm_vmscan_writepage, show_reclaim_flags(__entry->reclaim_flags)) ); +TRACE_EVENT(mm_vmscan_lru_shrink_inactive, + + TP_PROTO(int nid, int zid, + unsigned long nr_scanned, unsigned long nr_reclaimed, + int priority, int reclaim_flags), + + TP_ARGS(nid, zid, nr_scanned, nr_reclaimed, priority, reclaim_flags), + + TP_STRUCT__entry( + __field(int, nid) + __field(int, zid) + __field(unsigned long, nr_scanned) + __field(unsigned long, nr_reclaimed) + __field(int, priority) + __field(int, reclaim_flags) + ), + + TP_fast_assign( + __entry->nid = nid; + __entry->zid = zid; + __entry->nr_scanned = nr_scanned; + __entry->nr_reclaimed = nr_reclaimed; + __entry->priority = priority; + __entry->reclaim_flags = reclaim_flags; + ), + + TP_printk("nid=%d zid=%d nr_scanned=%ld nr_reclaimed=%ld priority=%d flags=%s", + __entry->nid, __entry->zid, + __entry->nr_scanned, __entry->nr_reclaimed, + __entry->priority, + show_reclaim_flags(__entry->reclaim_flags)) +); + + #endif /* _TRACE_VMSCAN_H */ /* This part must be outside protection */ diff --git a/mm/vmscan.c b/mm/vmscan.c index 28b0521e4bfd..4a6dccb57586 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1358,6 +1358,12 @@ shrink_inactive_list(unsigned long nr_to_scan, struct zone *zone, __count_zone_vm_events(PGSTEAL, zone, nr_reclaimed); putback_lru_pages(zone, sc, nr_anon, nr_file, &page_list); + + trace_mm_vmscan_lru_shrink_inactive(zone->zone_pgdat->node_id, + zone_idx(zone), + nr_scanned, nr_reclaimed, + priority, + trace_shrink_flags(file, sc->lumpy_reclaim_mode)); return nr_reclaimed; } -- cgit v1.2.3-70-g09d2 From d65bfacb046f3df8aa11a9cb9b6e448f6171174d Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 26 Oct 2010 14:21:54 -0700 Subject: mm: highmem documentation Document outlining some of the highmem issues, started by me, edited by David. Signed-off-by: David Howells Signed-off-by: Peter Zijlstra Acked-by: Chris Metcalf Cc: Hugh Dickins Cc: Rik van Riel Cc: Ingo Molnar Cc: Thomas Gleixner Cc: "H. Peter Anvin" Cc: Steven Rostedt Cc: Russell King Cc: Ralf Baechle Cc: David Miller Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/vm/highmem.txt | 162 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 Documentation/vm/highmem.txt (limited to 'Documentation') diff --git a/Documentation/vm/highmem.txt b/Documentation/vm/highmem.txt new file mode 100644 index 000000000000..4324d24ffacd --- /dev/null +++ b/Documentation/vm/highmem.txt @@ -0,0 +1,162 @@ + + ==================== + HIGH MEMORY HANDLING + ==================== + +By: Peter Zijlstra + +Contents: + + (*) What is high memory? + + (*) Temporary virtual mappings. + + (*) Using kmap_atomic. + + (*) Cost of temporary mappings. + + (*) i386 PAE. + + +==================== +WHAT IS HIGH MEMORY? +==================== + +High memory (highmem) is used when the size of physical memory approaches or +exceeds the maximum size of virtual memory. At that point it becomes +impossible for the kernel to keep all of the available physical memory mapped +at all times. This means the kernel needs to start using temporary mappings of +the pieces of physical memory that it wants to access. + +The part of (physical) memory not covered by a permanent mapping is what we +refer to as 'highmem'. There are various architecture dependent constraints on +where exactly that border lies. + +In the i386 arch, for example, we choose to map the kernel into every process's +VM space so that we don't have to pay the full TLB invalidation costs for +kernel entry/exit. This means the available virtual memory space (4GiB on +i386) has to be divided between user and kernel space. + +The traditional split for architectures using this approach is 3:1, 3GiB for +userspace and the top 1GiB for kernel space: + + +--------+ 0xffffffff + | Kernel | + +--------+ 0xc0000000 + | | + | User | + | | + +--------+ 0x00000000 + +This means that the kernel can at most map 1GiB of physical memory at any one +time, but because we need virtual address space for other things - including +temporary maps to access the rest of the physical memory - the actual direct +map will typically be less (usually around ~896MiB). + +Other architectures that have mm context tagged TLBs can have separate kernel +and user maps. Some hardware (like some ARMs), however, have limited virtual +space when they use mm context tags. + + +========================== +TEMPORARY VIRTUAL MAPPINGS +========================== + +The kernel contains several ways of creating temporary mappings: + + (*) vmap(). This can be used to make a long duration mapping of multiple + physical pages into a contiguous virtual space. It needs global + synchronization to unmap. + + (*) kmap(). This permits a short duration mapping of a single page. It needs + global synchronization, but is amortized somewhat. It is also prone to + deadlocks when using in a nested fashion, and so it is not recommended for + new code. + + (*) kmap_atomic(). This permits a very short duration mapping of a single + page. Since the mapping is restricted to the CPU that issued it, it + performs well, but the issuing task is therefore required to stay on that + CPU until it has finished, lest some other task displace its mappings. + + kmap_atomic() may also be used by interrupt contexts, since it is does not + sleep and the caller may not sleep until after kunmap_atomic() is called. + + It may be assumed that k[un]map_atomic() won't fail. + + +================= +USING KMAP_ATOMIC +================= + +When and where to use kmap_atomic() is straightforward. It is used when code +wants to access the contents of a page that might be allocated from high memory +(see __GFP_HIGHMEM), for example a page in the pagecache. The API has two +functions, and they can be used in a manner similar to the following: + + /* Find the page of interest. */ + struct page *page = find_get_page(mapping, offset); + + /* Gain access to the contents of that page. */ + void *vaddr = kmap_atomic(page); + + /* Do something to the contents of that page. */ + memset(vaddr, 0, PAGE_SIZE); + + /* Unmap that page. */ + kunmap_atomic(vaddr); + +Note that the kunmap_atomic() call takes the result of the kmap_atomic() call +not the argument. + +If you need to map two pages because you want to copy from one page to +another you need to keep the kmap_atomic calls strictly nested, like: + + vaddr1 = kmap_atomic(page1); + vaddr2 = kmap_atomic(page2); + + memcpy(vaddr1, vaddr2, PAGE_SIZE); + + kunmap_atomic(vaddr2); + kunmap_atomic(vaddr1); + + +========================== +COST OF TEMPORARY MAPPINGS +========================== + +The cost of creating temporary mappings can be quite high. The arch has to +manipulate the kernel's page tables, the data TLB and/or the MMU's registers. + +If CONFIG_HIGHMEM is not set, then the kernel will try and create a mapping +simply with a bit of arithmetic that will convert the page struct address into +a pointer to the page contents rather than juggling mappings about. In such a +case, the unmap operation may be a null operation. + +If CONFIG_MMU is not set, then there can be no temporary mappings and no +highmem. In such a case, the arithmetic approach will also be used. + + +======== +i386 PAE +======== + +The i386 arch, under some circumstances, will permit you to stick up to 64GiB +of RAM into your 32-bit machine. This has a number of consequences: + + (*) Linux needs a page-frame structure for each page in the system and the + pageframes need to live in the permanent mapping, which means: + + (*) you can have 896M/sizeof(struct page) page-frames at most; with struct + page being 32-bytes that would end up being something in the order of 112G + worth of pages; the kernel, however, needs to store more than just + page-frames in that memory... + + (*) PAE makes your page tables larger - which slows the system down as more + data has to be accessed to traverse in TLB fills and the like. One + advantage is that PAE has more PTE bits and can provide advanced features + like NX and PAT. + +The general recommendation is that you don't use more than 8GiB on a 32-bit +machine - although more might work for you and your workload, you're pretty +much on your own - don't expect kernel developers to really care much if things +come apart. -- cgit v1.2.3-70-g09d2 From aaaddfe0b3bb449b8734bf29bbd36141076e5277 Mon Sep 17 00:00:00 2001 From: Jaswinder Singh Rajput Date: Tue, 26 Oct 2010 14:22:14 -0700 Subject: Documentation/timers/hpet_example.c: add supporting info for hpet_example $./hpet_example info /dev/hpet -hpet: executing info hpet_info: hi_irqfreq 0x0 hi_flags 0x0 hi_hpet 0 hi_timer 2 Signed-off-by: Jaswinder Singh Rajput Cc: Clemens Ladisch Cc: "Venkatesh Pallipadi (Venki)" Cc: john stultz Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/timers/hpet_example.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'Documentation') diff --git a/Documentation/timers/hpet_example.c b/Documentation/timers/hpet_example.c index 4bfafb7bc4c5..9a3e7012c190 100644 --- a/Documentation/timers/hpet_example.c +++ b/Documentation/timers/hpet_example.c @@ -97,6 +97,33 @@ hpet_open_close(int argc, const char **argv) void hpet_info(int argc, const char **argv) { + struct hpet_info info; + int fd; + + if (argc != 1) { + fprintf(stderr, "hpet_info: device-name\n"); + return; + } + + fd = open(argv[0], O_RDONLY); + if (fd < 0) { + fprintf(stderr, "hpet_info: open of %s failed\n", argv[0]); + return; + } + + if (ioctl(fd, HPET_INFO, &info) < 0) { + fprintf(stderr, "hpet_info: failed to get info\n"); + goto out; + } + + fprintf(stderr, "hpet_info: hi_irqfreq 0x%lx hi_flags 0x%lx ", + info.hi_ireqfreq, info.hi_flags); + fprintf(stderr, "hi_hpet %d hi_timer %d\n", + info.hi_hpet, info.hi_timer); + +out: + close(fd); + return; } void -- cgit v1.2.3-70-g09d2 From 3f0f4a3f2008613c601e97f773dbd80ac400e459 Mon Sep 17 00:00:00 2001 From: Samu Onkalo Date: Tue, 26 Oct 2010 14:22:39 -0700 Subject: Documentation: short descriptions for bh1770glc and apds990x drivers Add short documentation for two ALS / proximity chip drivers. Signed-off-by: Samu Onkalo Acked-by: Jonathan Cameron Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/misc-devices/apds990x.txt | 111 +++++++++++++++++++++++++++++ Documentation/misc-devices/bh1770glc.txt | 116 +++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+) create mode 100644 Documentation/misc-devices/apds990x.txt create mode 100644 Documentation/misc-devices/bh1770glc.txt (limited to 'Documentation') diff --git a/Documentation/misc-devices/apds990x.txt b/Documentation/misc-devices/apds990x.txt new file mode 100644 index 000000000000..d5408cade32f --- /dev/null +++ b/Documentation/misc-devices/apds990x.txt @@ -0,0 +1,111 @@ +Kernel driver apds990x +====================== + +Supported chips: +Avago APDS990X + +Data sheet: +Not freely available + +Author: +Samu Onkalo + +Description +----------- + +APDS990x is a combined ambient light and proximity sensor. ALS and proximity +functionality are highly connected. ALS measurement path must be running +while the proximity functionality is enabled. + +ALS produces raw measurement values for two channels: Clear channel +(infrared + visible light) and IR only. However, threshold comparisons happen +using clear channel only. Lux value and the threshold level on the HW +might vary quite much depending the spectrum of the light source. + +Driver makes necessary conversions to both directions so that user handles +only lux values. Lux value is calculated using information from the both +channels. HW threshold level is calculated from the given lux value to match +with current type of the lightning. Sometimes inaccuracy of the estimations +lead to false interrupt, but that doesn't harm. + +ALS contains 4 different gain steps. Driver automatically +selects suitable gain step. After each measurement, reliability of the results +is estimated and new measurement is trigged if necessary. + +Platform data can provide tuned values to the conversion formulas if +values are known. Otherwise plain sensor default values are used. + +Proximity side is little bit simpler. There is no need for complex conversions. +It produces directly usable values. + +Driver controls chip operational state using pm_runtime framework. +Voltage regulators are controlled based on chip operational state. + +SYSFS +----- + + +chip_id + RO - shows detected chip type and version + +power_state + RW - enable / disable chip. Uses counting logic + 1 enables the chip + 0 disables the chip +lux0_input + RO - measured lux value + sysfs_notify called when threshold interrupt occurs + +lux0_sensor_range + RO - lux0_input max value. Actually never reaches since sensor tends + to saturate much before that. Real max value varies depending + on the light spectrum etc. + +lux0_rate + RW - measurement rate in Hz + +lux0_rate_avail + RO - supported measurement rates + +lux0_calibscale + RW - calibration value. Set to neutral value by default. + Output results are multiplied with calibscale / calibscale_default + value. + +lux0_calibscale_default + RO - neutral calibration value + +lux0_thresh_above_value + RW - HI level threshold value. All results above the value + trigs an interrupt. 65535 (i.e. sensor_range) disables the above + interrupt. + +lux0_thresh_below_value + RW - LO level threshold value. All results below the value + trigs an interrupt. 0 disables the below interrupt. + +prox0_raw + RO - measured proximity value + sysfs_notify called when threshold interrupt occurs + +prox0_sensor_range + RO - prox0_raw max value (1023) + +prox0_raw_en + RW - enable / disable proximity - uses counting logic + 1 enables the proximity + 0 disables the proximity + +prox0_reporting_mode + RW - trigger / periodic. In "trigger" mode the driver tells two possible + values: 0 or prox0_sensor_range value. 0 means no proximity, + 1023 means proximity. This causes minimal number of interrupts. + In "periodic" mode the driver reports all values above + prox0_thresh_above. This causes more interrupts, but it can give + _rough_ estimate about the distance. + +prox0_reporting_mode_avail + RO - accepted values to prox0_reporting_mode (trigger, periodic) + +prox0_thresh_above_value + RW - threshold level which trigs proximity events. diff --git a/Documentation/misc-devices/bh1770glc.txt b/Documentation/misc-devices/bh1770glc.txt new file mode 100644 index 000000000000..7d64c014dc70 --- /dev/null +++ b/Documentation/misc-devices/bh1770glc.txt @@ -0,0 +1,116 @@ +Kernel driver bh1770glc +======================= + +Supported chips: +ROHM BH1770GLC +OSRAM SFH7770 + +Data sheet: +Not freely available + +Author: +Samu Onkalo + +Description +----------- +BH1770GLC and SFH7770 are combined ambient light and proximity sensors. +ALS and proximity parts operates on their own, but they shares common I2C +interface and interrupt logic. In principle they can run on their own, +but ALS side results are used to estimate reliability of the proximity sensor. + +ALS produces 16 bit lux values. The chip contains interrupt logic to produce +low and high threshold interrupts. + +Proximity part contains IR-led driver up to 3 IR leds. The chip measures +amount of reflected IR light and produces proximity result. Resolution is +8 bit. Driver supports only one channel. Driver uses ALS results to estimate +reliability of the proximity results. Thus ALS is always running while +proximity detection is needed. + +Driver uses threshold interrupts to avoid need for polling the values. +Proximity low interrupt doesn't exists in the chip. This is simulated +by using a delayed work. As long as there is proximity threshold above +interrupts the delayed work is pushed forward. So, when proximity level goes +below the threshold value, there is no interrupt and the delayed work will +finally run. This is handled as no proximity indication. + +Chip state is controlled via runtime pm framework when enabled in config. + +Calibscale factor is used to hide differences between the chips. By default +value set to neutral state meaning factor of 1.00. To get proper values, +calibrated source of light is needed as a reference. Calibscale factor is set +so that measurement produces about the expected lux value. + +SYSFS +----- + +chip_id + RO - shows detected chip type and version + +power_state + RW - enable / disable chip. Uses counting logic + 1 enables the chip + 0 disables the chip + +lux0_input + RO - measured lux value + sysfs_notify called when threshold interrupt occurs + +lux0_sensor_range + RO - lux0_input max value + +lux0_rate + RW - measurement rate in Hz + +lux0_rate_avail + RO - supported measurement rates + +lux0_thresh_above_value + RW - HI level threshold value. All results above the value + trigs an interrupt. 65535 (i.e. sensor_range) disables the above + interrupt. + +lux0_thresh_below_value + RW - LO level threshold value. All results below the value + trigs an interrupt. 0 disables the below interrupt. + +lux0_calibscale + RW - calibration value. Set to neutral value by default. + Output results are multiplied with calibscale / calibscale_default + value. + +lux0_calibscale_default + RO - neutral calibration value + +prox0_raw + RO - measured proximity value + sysfs_notify called when threshold interrupt occurs + +prox0_sensor_range + RO - prox0_raw max value + +prox0_raw_en + RW - enable / disable proximity - uses counting logic + 1 enables the proximity + 0 disables the proximity + +prox0_thresh_above_count + RW - number of proximity interrupts needed before triggering the event + +prox0_rate_above + RW - Measurement rate (in Hz) when the level is above threshold + i.e. when proximity on has been reported. + +prox0_rate_below + RW - Measurement rate (in Hz) when the level is below threshold + i.e. when proximity off has been reported. + +prox0_rate_avail + RO - Supported proximity measurement rates in Hz + +prox0_thresh_above0_value + RW - threshold level which trigs proximity events. + Filtered by persistence filter (prox0_thresh_above_count) + +prox0_thresh_above1_value + RW - threshold level which trigs event immediately -- cgit v1.2.3-70-g09d2 From 003bb8ab7cecbfd1830bbccf9c6a9e2047d27c5c Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 26 Oct 2010 14:20:40 -0700 Subject: documentation: update sysrq.txt magic sysrq keys Update Documentation/sysrq.txt magic sysrq keys: - 'g' is for kgdb (not arch-specific); - add 2 new uses for 'v', remove the Voyager info; - add 'y' info (SPARC-64 specific); Signed-off-by: Randy Dunlap Cc: Jason Wessel Cc: "David S. Miller" Cc: "James E.J. Bottomley" Cc: David Airlie Acked-by: Alexander Shishkin Signed-off-by: Linus Torvalds --- Documentation/sysrq.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sysrq.txt b/Documentation/sysrq.txt index 5c17196c8fe9..312e3754e8c5 100644 --- a/Documentation/sysrq.txt +++ b/Documentation/sysrq.txt @@ -75,7 +75,7 @@ On all - write a character to /proc/sysrq-trigger. e.g.: 'f' - Will call oom_kill to kill a memory hog process. -'g' - Used by kgdb on ppc and sh platforms. +'g' - Used by kgdb (kernel debugger) 'h' - Will display help (actually any other key than those listed here will display help. but 'h' is easy to remember :-) @@ -110,12 +110,15 @@ On all - write a character to /proc/sysrq-trigger. e.g.: 'u' - Will attempt to remount all mounted filesystems read-only. -'v' - Dumps Voyager SMP processor info to your console. +'v' - Forcefully restores framebuffer console +'v' - Causes ETM buffer dump [ARM-specific] 'w' - Dumps tasks that are in uninterruptable (blocked) state. 'x' - Used by xmon interface on ppc/powerpc platforms. +'y' - Show global CPU Registers [SPARC-64 specific] + 'z' - Dump the ftrace buffer '0'-'9' - Sets the console log level, controlling which kernel messages -- cgit v1.2.3-70-g09d2 From ee2f154a598e96df2ebb01648a7699373bc085c7 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 26 Oct 2010 14:17:25 -0700 Subject: docbook: add more wait/wake/completion to device-drivers docbook Add more wait, wake, and completion interfaces to the device-drivers docbook. Fix kernel-doc notation in the added files. Signed-off-by: Randy Dunlap Signed-off-by: Linus Torvalds --- Documentation/DocBook/device-drivers.tmpl | 5 +++++ include/linux/completion.h | 10 +++++----- kernel/wait.c | 6 +++--- 3 files changed, 13 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index feca0758391e..22edcbb9ddaf 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -51,7 +51,12 @@ Delaying, scheduling, and timer routines !Iinclude/linux/sched.h !Ekernel/sched.c +!Iinclude/linux/completion.h !Ekernel/timer.c + + Wait queues and Wake events +!Iinclude/linux/wait.h +!Ekernel/wait.c High-resolution timers !Iinclude/linux/ktime.h diff --git a/include/linux/completion.h b/include/linux/completion.h index 51e3145196f6..36d57f74cd01 100644 --- a/include/linux/completion.h +++ b/include/linux/completion.h @@ -10,7 +10,7 @@ #include -/** +/* * struct completion - structure used to maintain state for a "completion" * * This is the opaque structure used to maintain the state for a "completion". @@ -34,7 +34,7 @@ struct completion { ({ init_completion(&work); work; }) /** - * DECLARE_COMPLETION: - declare and initialize a completion structure + * DECLARE_COMPLETION - declare and initialize a completion structure * @work: identifier for the completion structure * * This macro declares and initializes a completion structure. Generally used @@ -50,7 +50,7 @@ struct completion { * are on the kernel stack: */ /** - * DECLARE_COMPLETION_ONSTACK: - declare and initialize a completion structure + * DECLARE_COMPLETION_ONSTACK - declare and initialize a completion structure * @work: identifier for the completion structure * * This macro declares and initializes a completion structure on the kernel @@ -64,7 +64,7 @@ struct completion { #endif /** - * init_completion: - Initialize a dynamically allocated completion + * init_completion - Initialize a dynamically allocated completion * @x: completion structure that is to be initialized * * This inline function will initialize a dynamically created completion @@ -92,7 +92,7 @@ extern void complete(struct completion *); extern void complete_all(struct completion *); /** - * INIT_COMPLETION: - reinitialize a completion structure + * INIT_COMPLETION - reinitialize a completion structure * @x: completion structure to be reinitialized * * This macro should be used to reinitialize a completion structure so it can diff --git a/kernel/wait.c b/kernel/wait.c index c4bd3d825f35..b0310eb6cc1e 100644 --- a/kernel/wait.c +++ b/kernel/wait.c @@ -92,7 +92,7 @@ prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state) } EXPORT_SYMBOL(prepare_to_wait_exclusive); -/* +/** * finish_wait - clean up after waiting in a queue * @q: waitqueue waited on * @wait: wait descriptor @@ -127,11 +127,11 @@ void finish_wait(wait_queue_head_t *q, wait_queue_t *wait) } EXPORT_SYMBOL(finish_wait); -/* +/** * abort_exclusive_wait - abort exclusive waiting in a queue * @q: waitqueue waited on * @wait: wait descriptor - * @state: runstate of the waiter to be woken + * @mode: runstate of the waiter to be woken * @key: key to identify a wait bit queue or %NULL * * Sets current thread back to running state and removes -- cgit v1.2.3-70-g09d2 From 56083ab17e0075e538270823c374b59cc97e73b9 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 26 Oct 2010 14:19:08 -0700 Subject: docbook: add idr/ida to kernel-api docbook Add idr/ida to kernel-api docbook. Fix typos and kernel-doc notation. Signed-off-by: Randy Dunlap Acked-by: Tejun Heo Cc: Naohiro Aota Cc: Jiri Kosina Signed-off-by: Linus Torvalds --- Documentation/DocBook/kernel-api.tmpl | 6 +++++ include/linux/idr.h | 1 + lib/idr.c | 49 ++++++++++++++++++----------------- 3 files changed, 32 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index 6b4e07f28b69..7160652a8736 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -93,6 +93,12 @@ X!Ilib/string.c !Elib/crc32.c !Elib/crc-ccitt.c + + idr/ida Functions +!Pinclude/linux/idr.h idr sync +!Plib/idr.c IDA description +!Elib/idr.c + diff --git a/include/linux/idr.h b/include/linux/idr.h index 928ae712709f..13a801f3d028 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -81,6 +81,7 @@ struct idr { #define _idr_rc_to_errno(rc) ((rc) == -1 ? -EAGAIN : -ENOSPC) /** + * DOC: idr sync * idr synchronization (stolen from radix-tree.h) * * idr_find() is able to be called locklessly, using RCU. The caller must diff --git a/lib/idr.c b/lib/idr.c index e35850d3004a..e15502e8b21e 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -106,7 +106,7 @@ static void idr_mark_full(struct idr_layer **pa, int id) } /** - * idr_pre_get - reserver resources for idr allocation + * idr_pre_get - reserve resources for idr allocation * @idp: idr handle * @gfp_mask: memory allocation flags * @@ -115,8 +115,8 @@ static void idr_mark_full(struct idr_layer **pa, int id) * caller should pass in GFP_KERNEL if possible. This of course requires that * no spinning locks be held. * - * If the system is REALLY out of memory this function returns 0, - * otherwise 1. + * If the system is REALLY out of memory this function returns %0, + * otherwise %1. */ int idr_pre_get(struct idr *idp, gfp_t gfp_mask) { @@ -292,12 +292,12 @@ static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id) * required locks. * * If allocation from IDR's private freelist fails, idr_get_new_above() will - * return -EAGAIN. The caller should retry the idr_pre_get() call to refill + * return %-EAGAIN. The caller should retry the idr_pre_get() call to refill * IDR's preallocation and then retry the idr_get_new_above() call. * - * If the idr is full idr_get_new_above() will return -ENOSPC. + * If the idr is full idr_get_new_above() will return %-ENOSPC. * - * @id returns a value in the range @starting_id ... 0x7fffffff + * @id returns a value in the range @starting_id ... %0x7fffffff */ int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id) { @@ -322,12 +322,12 @@ EXPORT_SYMBOL(idr_get_new_above); * @id: pointer to the allocated handle * * If allocation from IDR's private freelist fails, idr_get_new_above() will - * return -EAGAIN. The caller should retry the idr_pre_get() call to refill + * return %-EAGAIN. The caller should retry the idr_pre_get() call to refill * IDR's preallocation and then retry the idr_get_new_above() call. * - * If the idr is full idr_get_new_above() will return -ENOSPC. + * If the idr is full idr_get_new_above() will return %-ENOSPC. * - * @id returns a value in the range 0 ... 0x7fffffff + * @id returns a value in the range %0 ... %0x7fffffff */ int idr_get_new(struct idr *idp, void *ptr, int *id) { @@ -390,7 +390,7 @@ static void sub_remove(struct idr *idp, int shift, int id) } /** - * idr_remove - remove the given id and free it's slot + * idr_remove - remove the given id and free its slot * @idp: idr handle * @id: unique key */ @@ -439,7 +439,7 @@ EXPORT_SYMBOL(idr_remove); * function will remove all id mappings and leave all idp_layers * unused. * - * A typical clean-up sequence for objects stored in an idr tree, will + * A typical clean-up sequence for objects stored in an idr tree will * use idr_for_each() to free all objects, if necessay, then * idr_remove_all() to remove all ids, and idr_destroy() to free * up the cached idr_layers. @@ -544,7 +544,7 @@ EXPORT_SYMBOL(idr_find); * not allowed. * * We check the return of @fn each time. If it returns anything other - * than 0, we break out and return that value. + * than %0, we break out and return that value. * * The caller must serialize idr_for_each() vs idr_get_new() and idr_remove(). */ @@ -639,8 +639,8 @@ EXPORT_SYMBOL(idr_get_next); * @id: lookup key * * Replace the pointer registered with an id and return the old value. - * A -ENOENT return indicates that @id was not found. - * A -EINVAL return indicates that @id was not within valid constraints. + * A %-ENOENT return indicates that @id was not found. + * A %-EINVAL return indicates that @id was not within valid constraints. * * The caller must serialize with writers. */ @@ -698,10 +698,11 @@ void idr_init(struct idr *idp) EXPORT_SYMBOL(idr_init); -/* +/** + * DOC: IDA description * IDA - IDR based ID allocator * - * this is id allocator without id -> pointer translation. Memory + * This is id allocator without id -> pointer translation. Memory * usage is much lower than full blown idr because each id only * occupies a bit. ida uses a custom leaf node which contains * IDA_BITMAP_BITS slots. @@ -734,8 +735,8 @@ static void free_bitmap(struct ida *ida, struct ida_bitmap *bitmap) * following function. It preallocates enough memory to satisfy the * worst possible allocation. * - * If the system is REALLY out of memory this function returns 0, - * otherwise 1. + * If the system is REALLY out of memory this function returns %0, + * otherwise %1. */ int ida_pre_get(struct ida *ida, gfp_t gfp_mask) { @@ -767,11 +768,11 @@ EXPORT_SYMBOL(ida_pre_get); * Allocate new ID above or equal to @ida. It should be called with * any required locks. * - * If memory is required, it will return -EAGAIN, you should unlock + * If memory is required, it will return %-EAGAIN, you should unlock * and go back to the ida_pre_get() call. If the ida is full, it will - * return -ENOSPC. + * return %-ENOSPC. * - * @p_id returns a value in the range @starting_id ... 0x7fffffff. + * @p_id returns a value in the range @starting_id ... %0x7fffffff. */ int ida_get_new_above(struct ida *ida, int starting_id, int *p_id) { @@ -853,11 +854,11 @@ EXPORT_SYMBOL(ida_get_new_above); * * Allocate new ID. It should be called with any required locks. * - * If memory is required, it will return -EAGAIN, you should unlock + * If memory is required, it will return %-EAGAIN, you should unlock * and go back to the idr_pre_get() call. If the idr is full, it will - * return -ENOSPC. + * return %-ENOSPC. * - * @id returns a value in the range 0 ... 0x7fffffff. + * @id returns a value in the range %0 ... %0x7fffffff. */ int ida_get_new(struct ida *ida, int *p_id) { -- cgit v1.2.3-70-g09d2 From 9dcf7990c2b8afe865d59f809a35a84d10f241f8 Mon Sep 17 00:00:00 2001 From: Nicolas Palix Date: Sun, 24 Oct 2010 23:37:33 +0200 Subject: Coccinelle: Fix documentation A file used as example has been moved elsewhere. Update the documentation accordingly Signed-off-by: Nicolas Palix Reported-by: Julia Lawall Signed-off-by: Michal Marek --- Documentation/coccinelle.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/coccinelle.txt b/Documentation/coccinelle.txt index de51a3eb17b3..4a276ea7001c 100644 --- a/Documentation/coccinelle.txt +++ b/Documentation/coccinelle.txt @@ -139,7 +139,7 @@ Example: Running - make coccicheck MODE=report COCCI=scripts/coccinelle/err_cast.cocci + make coccicheck MODE=report COCCI=scripts/coccinelle/api/err_cast.cocci will execute the following part of the SmPL script. @@ -177,7 +177,7 @@ identified. Example: Running - make coccicheck MODE=patch COCCI=scripts/coccinelle/err_cast.cocci + make coccicheck MODE=patch COCCI=scripts/coccinelle/api/err_cast.cocci will execute the following part of the SmPL script. @@ -221,7 +221,7 @@ NOTE: The diff-like output generated is NOT an applicable patch. The Example: Running - make coccicheck MODE=context COCCI=scripts/coccinelle/err_cast.cocci + make coccicheck MODE=context COCCI=scripts/coccinelle/api/err_cast.cocci will execute the following part of the SmPL script. @@ -256,7 +256,7 @@ diff -u -p /home/user/linux/crypto/ctr.c /tmp/nothing Example: Running - make coccicheck MODE=org COCCI=scripts/coccinelle/err_cast.cocci + make coccicheck MODE=org COCCI=scripts/coccinelle/api/err_cast.cocci will execute the following part of the SmPL script. -- cgit v1.2.3-70-g09d2 From abffc0207f12563f17bbde96e4cc0d9f3d7e2a53 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Wed, 27 Oct 2010 15:33:31 -0700 Subject: doc: clarify the behaviour of dirty_ratio/dirty_bytes When dirty_ratio or dirty_bytes is written the other parameter is disabled and set to 0 (in dirty_bytes_handler() / dirty_ratio_handler()). We do the same for dirty_background_ratio and dirty_background_bytes. However, in the sysctl documentation, we say that the counterpart becomes a function of the old value, that is not correct. Clarify the documentation reporting the actual behaviour. Reviewed-by: Greg Thelen Acked-by: David Rientjes Signed-off-by: Andrea Righi Cc: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/sysctl/vm.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index b606c2c4dd37..30289fab86eb 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -80,8 +80,10 @@ dirty_background_bytes Contains the amount of dirty memory at which the pdflush background writeback daemon will start writeback. -If dirty_background_bytes is written, dirty_background_ratio becomes a function -of its value (dirty_background_bytes / the amount of dirtyable system memory). +Note: dirty_background_bytes is the counterpart of dirty_background_ratio. Only +one of them may be specified at a time. When one sysctl is written it is +immediately taken into account to evaluate the dirty memory limits and the +other appears as 0 when read. ============================================================== @@ -97,8 +99,10 @@ dirty_bytes Contains the amount of dirty memory at which a process generating disk writes will itself start writeback. -If dirty_bytes is written, dirty_ratio becomes a function of its value -(dirty_bytes / the amount of dirtyable system memory). +Note: dirty_bytes is the counterpart of dirty_ratio. Only one of them may be +specified at a time. When one sysctl is written it is immediately taken into +account to evaluate the dirty memory limits and the other appears as 0 when +read. Note: the minimum value allowed for dirty_bytes is two pages (in bytes); any value lower than this limit will be ignored and the old configuration will be -- cgit v1.2.3-70-g09d2 From 97978e6d1f2da0073416870410459694fbdbfd9b Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Wed, 27 Oct 2010 15:33:35 -0700 Subject: cgroup: add clone_children control file The ns_cgroup is a control group interacting with the namespaces. When a new namespace is created, a corresponding cgroup is automatically created too. The cgroup name is the pid of the process who did 'unshare' or the child of 'clone'. This cgroup is tied with the namespace because it prevents a process to escape the control group and use the post_clone callback, so the child cgroup inherits the values of the parent cgroup. Unfortunately, the more we use this cgroup and the more we are facing problems with it: (1) when a process unshares, the cgroup name may conflict with a previous cgroup with the same pid, so unshare or clone return -EEXIST (2) the cgroup creation is out of control because there may have an application creating several namespaces where the system will automatically create several cgroups in his back and let them on the cgroupfs (eg. a vrf based on the network namespace). (3) the mix of (1) and (2) force an administrator to regularly check and clean these cgroups. This patchset removes the ns_cgroup by adding a new flag to the cgroup and the cgroupfs mount option. It enables the copy of the parent cgroup when a child cgroup is created. We can then safely remove the ns_cgroup as this flag brings a compatibility. We have now to manually create and add the task to a cgroup, which is consistent with the cgroup framework. This patch: Sent as an answer to a previous thread around the ns_cgroup. https://lists.linux-foundation.org/pipermail/containers/2009-June/018627.html It adds a control file 'clone_children' for a cgroup. This control file is a boolean specifying if the child cgroup should be a clone of the parent cgroup or not. The default value is 'false'. This flag makes the child cgroup to call the post_clone callback of all the subsystem, if it is available. At present, the cpuset is the only one which had implemented the post_clone callback. The option can be set at mount time by specifying the 'clone_children' mount option. Signed-off-by: Daniel Lezcano Signed-off-by: Serge E. Hallyn Cc: Eric W. Biederman Acked-by: Paul Menage Reviewed-by: Li Zefan Cc: Jamal Hadi Salim Cc: Matt Helsley Acked-by: Balbir Singh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/cgroups/cgroups.txt | 14 ++++++++++++-- include/linux/cgroup.h | 4 ++++ kernel/cgroup.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt index b34823ff1646..190018b0c649 100644 --- a/Documentation/cgroups/cgroups.txt +++ b/Documentation/cgroups/cgroups.txt @@ -18,7 +18,8 @@ CONTENTS: 1.2 Why are cgroups needed ? 1.3 How are cgroups implemented ? 1.4 What does notify_on_release do ? - 1.5 How do I use cgroups ? + 1.5 What does clone_children do ? + 1.6 How do I use cgroups ? 2. Usage Examples and Syntax 2.1 Basic Usage 2.2 Attaching processes @@ -293,7 +294,16 @@ notify_on_release in the root cgroup at system boot is disabled value of their parents notify_on_release setting. The default value of a cgroup hierarchy's release_agent path is empty. -1.5 How do I use cgroups ? +1.5 What does clone_children do ? +--------------------------------- + +If the clone_children flag is enabled (1) in a cgroup, then all +cgroups created beneath will call the post_clone callbacks for each +subsystem of the newly created cgroup. Usually when this callback is +implemented for a subsystem, it copies the values of the parent +subsystem, this is the case for the cpuset. + +1.6 How do I use cgroups ? -------------------------- To start a new job that is to be contained within a cgroup, using diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 709dfb901d11..ed4ba111bc8d 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -154,6 +154,10 @@ enum { * A thread in rmdir() is wating for this cgroup. */ CGRP_WAIT_ON_RMDIR, + /* + * Clone cgroup values when creating a new child cgroup + */ + CGRP_CLONE_CHILDREN, }; /* which pidlist file are we talking about? */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 9270d532ec3c..4b218a46ddd3 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -243,6 +243,11 @@ static int notify_on_release(const struct cgroup *cgrp) return test_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags); } +static int clone_children(const struct cgroup *cgrp) +{ + return test_bit(CGRP_CLONE_CHILDREN, &cgrp->flags); +} + /* * for_each_subsys() allows you to iterate on each subsystem attached to * an active hierarchy @@ -1040,6 +1045,8 @@ static int cgroup_show_options(struct seq_file *seq, struct vfsmount *vfs) seq_puts(seq, ",noprefix"); if (strlen(root->release_agent_path)) seq_printf(seq, ",release_agent=%s", root->release_agent_path); + if (clone_children(&root->top_cgroup)) + seq_puts(seq, ",clone_children"); if (strlen(root->name)) seq_printf(seq, ",name=%s", root->name); mutex_unlock(&cgroup_mutex); @@ -1050,6 +1057,7 @@ struct cgroup_sb_opts { unsigned long subsys_bits; unsigned long flags; char *release_agent; + bool clone_children; char *name; /* User explicitly requested empty subsystem */ bool none; @@ -1097,6 +1105,8 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts) opts->none = true; } else if (!strcmp(token, "noprefix")) { set_bit(ROOT_NOPREFIX, &opts->flags); + } else if (!strcmp(token, "clone_children")) { + opts->clone_children = true; } else if (!strncmp(token, "release_agent=", 14)) { /* Specifying two release agents is forbidden */ if (opts->release_agent) @@ -1355,6 +1365,8 @@ static struct cgroupfs_root *cgroup_root_from_opts(struct cgroup_sb_opts *opts) strcpy(root->release_agent_path, opts->release_agent); if (opts->name) strcpy(root->name, opts->name); + if (opts->clone_children) + set_bit(CGRP_CLONE_CHILDREN, &root->top_cgroup.flags); return root; } @@ -3173,6 +3185,23 @@ fail: return ret; } +static u64 cgroup_clone_children_read(struct cgroup *cgrp, + struct cftype *cft) +{ + return clone_children(cgrp); +} + +static int cgroup_clone_children_write(struct cgroup *cgrp, + struct cftype *cft, + u64 val) +{ + if (val) + set_bit(CGRP_CLONE_CHILDREN, &cgrp->flags); + else + clear_bit(CGRP_CLONE_CHILDREN, &cgrp->flags); + return 0; +} + /* * for the common functions, 'private' gives the type of file */ @@ -3203,6 +3232,11 @@ static struct cftype files[] = { .write_string = cgroup_write_event_control, .mode = S_IWUGO, }, + { + .name = "cgroup.clone_children", + .read_u64 = cgroup_clone_children_read, + .write_u64 = cgroup_clone_children_write, + }, }; static struct cftype cft_release_agent = { @@ -3332,6 +3366,9 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, if (notify_on_release(parent)) set_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags); + if (clone_children(parent)) + set_bit(CGRP_CLONE_CHILDREN, &cgrp->flags); + for_each_subsys(root, ss) { struct cgroup_subsys_state *css = ss->create(ss, cgrp); @@ -3346,6 +3383,8 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, goto err_destroy; } /* At error, ->destroy() callback has to free assigned ID. */ + if (clone_children(parent) && ss->post_clone) + ss->post_clone(ss, cgrp); } cgroup_lock_hierarchy(root); -- cgit v1.2.3-70-g09d2 From 45531757b45cae0ce64c5aff08c2534d5a0fa3e7 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Wed, 27 Oct 2010 15:33:38 -0700 Subject: cgroup: notify ns_cgroup deprecated The ns_cgroup will be removed very soon. Let's warn, for this version, ns_cgroup is deprecated. Make ns_cgroup and clone_children exclusive. If the clone_children is set and the ns_cgroup is mounted, let's fail with EINVAL when the ns_cgroup subsys is created (a printk will help the user to understand why the creation fails). Update the feature remove schedule file with the deprecated ns_cgroup. Signed-off-by: Daniel Lezcano Acked-by: Paul Menage Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/feature-removal-schedule.txt | 17 +++++++++++++++++ kernel/ns_cgroup.c | 8 ++++++++ 2 files changed, 25 insertions(+) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index d2af87ba96e1..f3da8c0a3af2 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -526,6 +526,23 @@ Who: FUJITA Tomonori ---------------------------- +What: namespace cgroup (ns_cgroup) +When: 2.6.38 +Why: The ns_cgroup leads to some problems: + * cgroup creation is out-of-control + * cgroup name can conflict when pids are looping + * it is not possible to have a single process handling + a lot of namespaces without falling in a exponential creation time + * we may want to create a namespace without creating a cgroup + + The ns_cgroup is replaced by a compatibility flag 'clone_children', + where a newly created cgroup will copy the parent cgroup values. + The userspace has to manually create a cgroup and add a task to + the 'tasks' file. +Who: Daniel Lezcano + +---------------------------- + What: iwlwifi disable_hw_scan module parameters When: 2.6.40 Why: Hareware scan is the prefer method for iwlwifi devices for diff --git a/kernel/ns_cgroup.c b/kernel/ns_cgroup.c index 2a5dfec8efe0..2c98ad94ba0e 100644 --- a/kernel/ns_cgroup.c +++ b/kernel/ns_cgroup.c @@ -85,6 +85,14 @@ static struct cgroup_subsys_state *ns_create(struct cgroup_subsys *ss, return ERR_PTR(-EPERM); if (!cgroup_is_descendant(cgroup, current)) return ERR_PTR(-EPERM); + if (test_bit(CGRP_CLONE_CHILDREN, &cgroup->flags)) { + printk("ns_cgroup can't be created with parent " + "'clone_children' set.\n"); + return ERR_PTR(-EINVAL); + } + + printk_once("ns_cgroup deprecated: consider using the " + "'clone_children' flag without the ns_cgroup.\n"); ns_cgroup = kzalloc(sizeof(*ns_cgroup), GFP_KERNEL); if (!ns_cgroup) -- cgit v1.2.3-70-g09d2 From b40d4f84becd69275451baee7f0801c85eb58437 Mon Sep 17 00:00:00 2001 From: Nikanth Karthikesan Date: Wed, 27 Oct 2010 15:34:10 -0700 Subject: /proc/pid/smaps: export amount of anonymous memory in a mapping Export the number of anonymous pages in a mapping via smaps. Even the private pages in a mapping backed by a file, would be marked as anonymous, when they are modified. Export this information to user-space via smaps. Exporting this count will help gdb to make a better decision on which areas need to be dumped in its coredump; and should be useful to others studying the memory usage of a process. Signed-off-by: Nikanth Karthikesan Acked-by: Hugh Dickins Reviewed-by: KOSAKI Motohiro Cc: Matt Mackall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/proc.txt | 13 ++++++++++--- fs/proc/task_mmu.c | 6 ++++++ 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index a563b74c7aef..976de6e19dd8 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -370,6 +370,7 @@ Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 0 kB Referenced: 892 kB +Anonymous: 0 kB Swap: 0 kB KernelPageSize: 4 kB MMUPageSize: 4 kB @@ -378,9 +379,15 @@ The first of these lines shows the same information as is displayed for the mapping in /proc/PID/maps. The remaining lines show the size of the mapping (size), the amount of the mapping that is currently resident in RAM (RSS), the process' proportional share of this mapping (PSS), the number of clean and -dirty shared pages in the mapping, and the number of clean and dirty private -pages in the mapping. The "Referenced" indicates the amount of memory -currently marked as referenced or accessed. +dirty private pages in the mapping. Note that even a page which is part of a +MAP_SHARED mapping, but has only a single pte mapped, i.e. is currently used +by only one process, is accounted as private and not as shared. "Referenced" +indicates the amount of memory currently marked as referenced or accessed. +"Anonymous" shows the amount of memory that does not belong to any file. Even +a mapping associated with a file may contain anonymous pages: when MAP_PRIVATE +and a page is modified, the file page is replaced by a private anonymous copy. +"Swap" shows how much would-be-anonymous memory is also used, but out on +swap. This file is only present if the CONFIG_MMU kernel configuration option is enabled. diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 871e25ed0069..da6b01d70f01 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -327,6 +327,7 @@ struct mem_size_stats { unsigned long private_clean; unsigned long private_dirty; unsigned long referenced; + unsigned long anonymous; unsigned long swap; u64 pss; }; @@ -357,6 +358,9 @@ static int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, if (!page) continue; + if (PageAnon(page)) + mss->anonymous += PAGE_SIZE; + mss->resident += PAGE_SIZE; /* Accumulate the size in pages that have been accessed. */ if (pte_young(ptent) || PageReferenced(page)) @@ -410,6 +414,7 @@ static int show_smap(struct seq_file *m, void *v) "Private_Clean: %8lu kB\n" "Private_Dirty: %8lu kB\n" "Referenced: %8lu kB\n" + "Anonymous: %8lu kB\n" "Swap: %8lu kB\n" "KernelPageSize: %8lu kB\n" "MMUPageSize: %8lu kB\n", @@ -421,6 +426,7 @@ static int show_smap(struct seq_file *m, void *v) mss.private_clean >> 10, mss.private_dirty >> 10, mss.referenced >> 10, + mss.anonymous >> 10, mss.swap >> 10, vma_kernel_pagesize(vma) >> 10, vma_mmu_pagesize(vma) >> 10); -- cgit v1.2.3-70-g09d2 From 03f890f8c2f5c9008d3d8f6d85267717ced4bd79 Mon Sep 17 00:00:00 2001 From: Nikanth Karthikesan Date: Wed, 27 Oct 2010 15:34:11 -0700 Subject: /proc/pid/pagemap: document in Documentation/filesystems/proc.txt Document /proc/pid/pagemap in Documentation/filesystems/proc.txt Signed-off-by: Nikanth Karthikesan Cc: Richard Guenther Cc: Balbir Singh Cc: KOSAKI Motohiro Acked-by: Matt Mackall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/proc.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Documentation') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 976de6e19dd8..e73df2722ff3 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -136,6 +136,7 @@ Table 1-1: Process specific entries in /proc statm Process memory status information status Process status in human readable form wchan If CONFIG_KALLSYMS is set, a pre-decoded wchan + pagemap Page table stack Report full stack trace, enable via CONFIG_STACKTRACE smaps a extension based on maps, showing the memory consumption of each mapping @@ -404,6 +405,9 @@ To clear the bits for the file mapped pages associated with the process > echo 3 > /proc/PID/clear_refs Any other value written to /proc/PID/clear_refs will have no effect. +The /proc/pid/pagemap gives the PFN, which can be used to find the pageflags +using /proc/kpageflags and number of times a page is mapped using +/proc/kpagecount. For detailed explanation, see Documentation/vm/pagemap.txt. 1.2 Kernel data --------------- -- cgit v1.2.3-70-g09d2 From db9e5679d6aecb17253f41bd06d98194800f9c01 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 27 Oct 2010 15:34:42 -0700 Subject: delay-accounting: reimplement -c for getdelays.c to report information on a target command Task delay-accounting was identified as one means of determining how long a process spends in congestion_wait() without adding new statistics. For example, if the workload should not be doing IO, delay-accounting could reveal how long it was spending in unexpected IO or delays. Unfortunately, on closer examination it was clear that getdelays does not act as documented. Commit a3baf649 ("per-task-delay-accounting: documentation") added Documentation/accounting/getdelays.c with a -c switch that was documented to fork/exec a child and report statistics on it but for reasons that are unclear to me, commit 9e06d3f9 deleted support for this switch but did not update the documentation. It might be an oversight or it might be because the control flow of the program meant that accounting information would be printed once early in the lifetime of the program making it of limited use. This patch reimplements -c for getdelays.c to act as documented. Unlike the original version, it waits until the command completes before printing any information on it. An example of it being used looks like $ ./getdelays -d -c find /home/mel -name mel print delayacct stats ON /home/mel /home/mel/.notes-wine/drive_c/windows/profiles/mel /home/mel/.wine/drive_c/windows/profiles/mel /home/mel/git-configs/dot.kde/share/apps/konqueror/home/mel PID 5923 CPU count real total virtual total delay total 42779 5051232096 5164722692 564207988 IO count delay total 41727 97804147758 SWAP count delay total 0 0 RECLAIM count delay total 0 0 [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Mel Gorman Acked-by: Balbir Singh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/accounting/getdelays.c | 38 ++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/accounting/getdelays.c b/Documentation/accounting/getdelays.c index 6e25c2659e0a..a2976a6de033 100644 --- a/Documentation/accounting/getdelays.c +++ b/Documentation/accounting/getdelays.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -266,11 +267,13 @@ int main(int argc, char *argv[]) int containerset = 0; char containerpath[1024]; int cfd = 0; + int forking = 0; + sigset_t sigset; struct msgtemplate msg; - while (1) { - c = getopt(argc, argv, "qdiw:r:m:t:p:vlC:"); + while (!forking) { + c = getopt(argc, argv, "qdiw:r:m:t:p:vlC:c:"); if (c < 0) break; @@ -319,6 +322,28 @@ int main(int argc, char *argv[]) err(1, "Invalid pid\n"); cmd_type = TASKSTATS_CMD_ATTR_PID; break; + case 'c': + + /* Block SIGCHLD for sigwait() later */ + if (sigemptyset(&sigset) == -1) + err(1, "Failed to empty sigset"); + if (sigaddset(&sigset, SIGCHLD)) + err(1, "Failed to set sigchld in sigset"); + sigprocmask(SIG_BLOCK, &sigset, NULL); + + /* fork/exec a child */ + tid = fork(); + if (tid < 0) + err(1, "Fork failed\n"); + if (tid == 0) + if (execvp(argv[optind - 1], + &argv[optind - 1]) < 0) + exit(-1); + + /* Set the command type and avoid further processing */ + cmd_type = TASKSTATS_CMD_ATTR_PID; + forking = 1; + break; case 'v': printf("debug on\n"); dbg = 1; @@ -370,6 +395,15 @@ int main(int argc, char *argv[]) goto err; } + /* + * If we forked a child, wait for it to exit. Cannot use waitpid() + * as all the delicious data would be reaped as part of the wait + */ + if (tid && forking) { + int sig_received; + sigwait(&sigset, &sig_received); + } + if (tid) { rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, cmd_type, &tid, sizeof(__u32)); -- cgit v1.2.3-70-g09d2 From bfff68738f1cb5c93dab1114634cea02aae9e7ba Mon Sep 17 00:00:00 2001 From: Lukas Czerner Date: Wed, 27 Oct 2010 21:30:05 -0400 Subject: ext4: add support for lazy inode table initialization When the lazy_itable_init extended option is passed to mke2fs, it considerably speeds up filesystem creation because inode tables are not zeroed out. The fact that parts of the inode table are uninitialized is not a problem so long as the block group descriptors, which contain information regarding how much of the inode table has been initialized, has not been corrupted However, if the block group checksums are not valid, e2fsck must scan the entire inode table, and the the old, uninitialized data could potentially cause e2fsck to report false problems. Hence, it is important for the inode tables to be initialized as soon as possble. This commit adds this feature so that mke2fs can safely use the lazy inode table initialization feature to speed up formatting file systems. This is done via a new new kernel thread called ext4lazyinit, which is created on demand and destroyed, when it is no longer needed. There is only one thread for all ext4 filesystems in the system. When the first filesystem with inititable mount option is mounted, ext4lazyinit thread is created, then the filesystem can register its request in the request list. This thread then walks through the list of requests picking up scheduled requests and invoking ext4_init_inode_table(). Next schedule time for the request is computed by multiplying the time it took to zero out last inode table with wait multiplier, which can be set with the (init_itable=n) mount option (default is 10). We are doing this so we do not take the whole I/O bandwidth. When the thread is no longer necessary (request list is empty) it frees the appropriate structures and exits (and can be created later later by another filesystem). We do not disturb regular inode allocations in any way, it just do not care whether the inode table is, or is not zeroed. But when zeroing, we have to skip used inodes, obviously. Also we should prevent new inode allocations from the group, while zeroing is on the way. For that we take write alloc_sem lock in ext4_init_inode_table() and read alloc_sem in the ext4_claim_inode, so when we are unlucky and allocator hits the group which is currently being zeroed, it just has to wait. This can be suppresed using the mount option no_init_itable. Signed-off-by: Lukas Czerner Signed-off-by: "Theodore Ts'o" --- Documentation/filesystems/ext4.txt | 14 ++ fs/ext4/ext4.h | 40 ++++ fs/ext4/ialloc.c | 120 ++++++++++ fs/ext4/super.c | 440 ++++++++++++++++++++++++++++++++++++- 4 files changed, 611 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt index e1def1786e50..6ab9442d7eeb 100644 --- a/Documentation/filesystems/ext4.txt +++ b/Documentation/filesystems/ext4.txt @@ -353,6 +353,20 @@ noauto_da_alloc replacing existing files via patterns such as system crashes before the delayed allocation blocks are forced to disk. +noinit_itable Do not initialize any uninitialized inode table + blocks in the background. This feature may be + used by installation CD's so that the install + process can complete as quickly as possible; the + inode table initialization process would then be + deferred until the next time the file system + is unmounted. + +init_itable=n The lazy itable init code will wait n times the + number of milliseconds it took to zero out the + previous block group's inode table. This + minimizes the impact on the systme performance + while file system's inode table is being initialized. + discard Controls whether ext4 should issue discard/TRIM nodiscard(*) commands to the underlying block device when blocks are freed. This is useful for SSD devices diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index b364b9df09b3..0fe078d368d0 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -890,6 +890,7 @@ struct ext4_inode_info { #define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */ #define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000 /* Block validity checking */ #define EXT4_MOUNT_DISCARD 0x40000000 /* Issue DISCARD requests */ +#define EXT4_MOUNT_INIT_INODE_TABLE 0x80000000 /* Initialize uninitialized itables */ #define clear_opt(o, opt) o &= ~EXT4_MOUNT_##opt #define set_opt(o, opt) o |= EXT4_MOUNT_##opt @@ -1173,6 +1174,11 @@ struct ext4_sb_info { /* timer for periodic error stats printing */ struct timer_list s_err_report; + + /* Lazy inode table initialization info */ + struct ext4_li_request *s_li_request; + /* Wait multiplier for lazy initialization thread */ + unsigned int s_li_wait_mult; }; static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb) @@ -1536,6 +1542,38 @@ void ext4_get_group_no_and_offset(struct super_block *sb, ext4_fsblk_t blocknr, extern struct proc_dir_entry *ext4_proc_root; +/* + * Timeout and state flag for lazy initialization inode thread. + */ +#define EXT4_DEF_LI_WAIT_MULT 10 +#define EXT4_DEF_LI_MAX_START_DELAY 5 +#define EXT4_LAZYINIT_QUIT 0x0001 +#define EXT4_LAZYINIT_RUNNING 0x0002 + +/* + * Lazy inode table initialization info + */ +struct ext4_lazy_init { + unsigned long li_state; + + wait_queue_head_t li_wait_daemon; + wait_queue_head_t li_wait_task; + struct timer_list li_timer; + struct task_struct *li_task; + + struct list_head li_request_list; + struct mutex li_list_mtx; +}; + +struct ext4_li_request { + struct super_block *lr_super; + struct ext4_sb_info *lr_sbi; + ext4_group_t lr_next_group; + struct list_head lr_request; + unsigned long lr_next_sched; + unsigned long lr_timeout; +}; + /* * Function prototypes */ @@ -1611,6 +1649,8 @@ extern unsigned ext4_init_inode_bitmap(struct super_block *sb, ext4_group_t group, struct ext4_group_desc *desc); extern void mark_bitmap_end(int start_bit, int end_bit, char *bitmap); +extern int ext4_init_inode_table(struct super_block *sb, + ext4_group_t group, int barrier); /* mballoc.c */ extern long ext4_mb_stats; diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 45853e0d1f21..e428f23215c0 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -107,6 +107,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group) desc = ext4_get_group_desc(sb, block_group, NULL); if (!desc) return NULL; + bitmap_blk = ext4_inode_bitmap(sb, desc); bh = sb_getblk(sb, bitmap_blk); if (unlikely(!bh)) { @@ -123,6 +124,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group) unlock_buffer(bh); return bh; } + ext4_lock_group(sb, block_group); if (desc->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) { ext4_init_inode_bitmap(sb, bh, block_group, desc); @@ -133,6 +135,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group) return bh; } ext4_unlock_group(sb, block_group); + if (buffer_uptodate(bh)) { /* * if not uninit if bh is uptodate, @@ -712,8 +715,17 @@ static int ext4_claim_inode(struct super_block *sb, { int free = 0, retval = 0, count; struct ext4_sb_info *sbi = EXT4_SB(sb); + struct ext4_group_info *grp = ext4_get_group_info(sb, group); struct ext4_group_desc *gdp = ext4_get_group_desc(sb, group, NULL); + /* + * We have to be sure that new inode allocation does not race with + * inode table initialization, because otherwise we may end up + * allocating and writing new inode right before sb_issue_zeroout + * takes place and overwriting our new inode with zeroes. So we + * take alloc_sem to prevent it. + */ + down_read(&grp->alloc_sem); ext4_lock_group(sb, group); if (ext4_set_bit(ino, inode_bitmap_bh->b_data)) { /* not a free inode */ @@ -724,6 +736,7 @@ static int ext4_claim_inode(struct super_block *sb, if ((group == 0 && ino < EXT4_FIRST_INO(sb)) || ino > EXT4_INODES_PER_GROUP(sb)) { ext4_unlock_group(sb, group); + up_read(&grp->alloc_sem); ext4_error(sb, "reserved inode or inode > inodes count - " "block_group = %u, inode=%lu", group, ino + group * EXT4_INODES_PER_GROUP(sb)); @@ -772,6 +785,7 @@ static int ext4_claim_inode(struct super_block *sb, gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp); err_ret: ext4_unlock_group(sb, group); + up_read(&grp->alloc_sem); return retval; } @@ -1205,3 +1219,109 @@ unsigned long ext4_count_dirs(struct super_block * sb) } return count; } + +/* + * Zeroes not yet zeroed inode table - just write zeroes through the whole + * inode table. Must be called without any spinlock held. The only place + * where it is called from on active part of filesystem is ext4lazyinit + * thread, so we do not need any special locks, however we have to prevent + * inode allocation from the current group, so we take alloc_sem lock, to + * block ext4_claim_inode until we are finished. + */ +extern int ext4_init_inode_table(struct super_block *sb, ext4_group_t group, + int barrier) +{ + struct ext4_group_info *grp = ext4_get_group_info(sb, group); + struct ext4_sb_info *sbi = EXT4_SB(sb); + struct ext4_group_desc *gdp = NULL; + struct buffer_head *group_desc_bh; + handle_t *handle; + ext4_fsblk_t blk; + int num, ret = 0, used_blks = 0; + unsigned long flags = BLKDEV_IFL_WAIT; + + /* This should not happen, but just to be sure check this */ + if (sb->s_flags & MS_RDONLY) { + ret = 1; + goto out; + } + + gdp = ext4_get_group_desc(sb, group, &group_desc_bh); + if (!gdp) + goto out; + + /* + * We do not need to lock this, because we are the only one + * handling this flag. + */ + if (gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_ZEROED)) + goto out; + + handle = ext4_journal_start_sb(sb, 1); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + goto out; + } + + down_write(&grp->alloc_sem); + /* + * If inode bitmap was already initialized there may be some + * used inodes so we need to skip blocks with used inodes in + * inode table. + */ + if (!(gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT))) + used_blks = DIV_ROUND_UP((EXT4_INODES_PER_GROUP(sb) - + ext4_itable_unused_count(sb, gdp)), + sbi->s_inodes_per_block); + + blk = ext4_inode_table(sb, gdp) + used_blks; + num = sbi->s_itb_per_group - used_blks; + + BUFFER_TRACE(group_desc_bh, "get_write_access"); + ret = ext4_journal_get_write_access(handle, + group_desc_bh); + if (ret) + goto err_out; + + if (unlikely(num > EXT4_INODES_PER_GROUP(sb))) { + ext4_error(sb, "Something is wrong with group %u\n" + "Used itable blocks: %d" + "Itable blocks per group: %lu\n", + group, used_blks, sbi->s_itb_per_group); + ret = 1; + goto err_out; + } + + /* + * Skip zeroout if the inode table is full. But we set the ZEROED + * flag anyway, because obviously, when it is full it does not need + * further zeroing. + */ + if (unlikely(num == 0)) + goto skip_zeroout; + + ext4_debug("going to zero out inode table in group %d\n", + group); + if (barrier) + flags |= BLKDEV_IFL_BARRIER; + ret = sb_issue_zeroout(sb, blk, num, GFP_NOFS, flags); + if (ret < 0) + goto err_out; + +skip_zeroout: + ext4_lock_group(sb, group); + gdp->bg_flags |= cpu_to_le16(EXT4_BG_INODE_ZEROED); + gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp); + ext4_unlock_group(sb, group); + + BUFFER_TRACE(group_desc_bh, + "call ext4_handle_dirty_metadata"); + ret = ext4_handle_dirty_metadata(handle, NULL, + group_desc_bh); + +err_out: + up_write(&grp->alloc_sem); + ext4_journal_stop(handle); +out: + return ret; +} diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 751997d2cefe..5066537e5a38 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -41,6 +41,9 @@ #include #include +#include +#include + #include "ext4.h" #include "ext4_jbd2.h" #include "xattr.h" @@ -52,6 +55,8 @@ struct proc_dir_entry *ext4_proc_root; static struct kset *ext4_kset; +struct ext4_lazy_init *ext4_li_info; +struct mutex ext4_li_mtx; static int ext4_load_journal(struct super_block *, struct ext4_super_block *, unsigned long journal_devnum); @@ -70,6 +75,8 @@ static void ext4_write_super(struct super_block *sb); static int ext4_freeze(struct super_block *sb); static int ext4_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt); +static void ext4_destroy_lazyinit_thread(void); +static void ext4_unregister_li_request(struct super_block *sb); #if !defined(CONFIG_EXT3_FS) && !defined(CONFIG_EXT3_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23) static struct file_system_type ext3_fs_type = { @@ -720,6 +727,7 @@ static void ext4_put_super(struct super_block *sb) } del_timer(&sbi->s_err_report); + ext4_unregister_li_request(sb); ext4_release_system_zone(sb); ext4_mb_release(sb); ext4_ext_release(sb); @@ -1046,6 +1054,12 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs) !(def_mount_opts & EXT4_DEFM_BLOCK_VALIDITY)) seq_puts(seq, ",block_validity"); + if (!test_opt(sb, INIT_INODE_TABLE)) + seq_puts(seq, ",noinit_inode_table"); + else if (sbi->s_li_wait_mult) + seq_printf(seq, ",init_inode_table=%u", + (unsigned) sbi->s_li_wait_mult); + ext4_show_quota_options(seq, sb); return 0; @@ -1220,6 +1234,7 @@ enum { Opt_inode_readahead_blks, Opt_journal_ioprio, Opt_dioread_nolock, Opt_dioread_lock, Opt_discard, Opt_nodiscard, + Opt_init_inode_table, Opt_noinit_inode_table, }; static const match_table_t tokens = { @@ -1290,6 +1305,9 @@ static const match_table_t tokens = { {Opt_dioread_lock, "dioread_lock"}, {Opt_discard, "discard"}, {Opt_nodiscard, "nodiscard"}, + {Opt_init_inode_table, "init_itable=%u"}, + {Opt_init_inode_table, "init_itable"}, + {Opt_noinit_inode_table, "noinit_itable"}, {Opt_err, NULL}, }; @@ -1760,6 +1778,20 @@ set_qf_format: case Opt_dioread_lock: clear_opt(sbi->s_mount_opt, DIOREAD_NOLOCK); break; + case Opt_init_inode_table: + set_opt(sbi->s_mount_opt, INIT_INODE_TABLE); + if (args[0].from) { + if (match_int(&args[0], &option)) + return 0; + } else + option = EXT4_DEF_LI_WAIT_MULT; + if (option < 0) + return 0; + sbi->s_li_wait_mult = option; + break; + case Opt_noinit_inode_table: + clear_opt(sbi->s_mount_opt, INIT_INODE_TABLE); + break; default: ext4_msg(sb, KERN_ERR, "Unrecognized mount option \"%s\" " @@ -1943,7 +1975,8 @@ int ext4_group_desc_csum_verify(struct ext4_sb_info *sbi, __u32 block_group, } /* Called at mount-time, super-block is locked */ -static int ext4_check_descriptors(struct super_block *sb) +static int ext4_check_descriptors(struct super_block *sb, + ext4_group_t *first_not_zeroed) { struct ext4_sb_info *sbi = EXT4_SB(sb); ext4_fsblk_t first_block = le32_to_cpu(sbi->s_es->s_first_data_block); @@ -1952,7 +1985,7 @@ static int ext4_check_descriptors(struct super_block *sb) ext4_fsblk_t inode_bitmap; ext4_fsblk_t inode_table; int flexbg_flag = 0; - ext4_group_t i; + ext4_group_t i, grp = sbi->s_groups_count; if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG)) flexbg_flag = 1; @@ -1968,6 +2001,10 @@ static int ext4_check_descriptors(struct super_block *sb) last_block = first_block + (EXT4_BLOCKS_PER_GROUP(sb) - 1); + if ((grp == sbi->s_groups_count) && + !(gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_ZEROED))) + grp = i; + block_bitmap = ext4_block_bitmap(sb, gdp); if (block_bitmap < first_block || block_bitmap > last_block) { ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: " @@ -2005,6 +2042,8 @@ static int ext4_check_descriptors(struct super_block *sb) if (!flexbg_flag) first_block += EXT4_BLOCKS_PER_GROUP(sb); } + if (NULL != first_not_zeroed) + *first_not_zeroed = grp; ext4_free_blocks_count_set(sbi->s_es, ext4_count_free_blocks(sb)); sbi->s_es->s_free_inodes_count =cpu_to_le32(ext4_count_free_inodes(sb)); @@ -2543,6 +2582,378 @@ static void print_daily_error_info(unsigned long arg) mod_timer(&sbi->s_err_report, jiffies + 24*60*60*HZ); /* Once a day */ } +static void ext4_lazyinode_timeout(unsigned long data) +{ + struct task_struct *p = (struct task_struct *)data; + wake_up_process(p); +} + +/* Find next suitable group and run ext4_init_inode_table */ +static int ext4_run_li_request(struct ext4_li_request *elr) +{ + struct ext4_group_desc *gdp = NULL; + ext4_group_t group, ngroups; + struct super_block *sb; + unsigned long timeout = 0; + int ret = 0; + + sb = elr->lr_super; + ngroups = EXT4_SB(sb)->s_groups_count; + + for (group = elr->lr_next_group; group < ngroups; group++) { + gdp = ext4_get_group_desc(sb, group, NULL); + if (!gdp) { + ret = 1; + break; + } + + if (!(gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_ZEROED))) + break; + } + + if (group == ngroups) + ret = 1; + + if (!ret) { + timeout = jiffies; + ret = ext4_init_inode_table(sb, group, + elr->lr_timeout ? 0 : 1); + if (elr->lr_timeout == 0) { + timeout = jiffies - timeout; + if (elr->lr_sbi->s_li_wait_mult) + timeout *= elr->lr_sbi->s_li_wait_mult; + else + timeout *= 20; + elr->lr_timeout = timeout; + } + elr->lr_next_sched = jiffies + elr->lr_timeout; + elr->lr_next_group = group + 1; + } + + return ret; +} + +/* + * Remove lr_request from the list_request and free the + * request tructure. Should be called with li_list_mtx held + */ +static void ext4_remove_li_request(struct ext4_li_request *elr) +{ + struct ext4_sb_info *sbi; + + if (!elr) + return; + + sbi = elr->lr_sbi; + + list_del(&elr->lr_request); + sbi->s_li_request = NULL; + kfree(elr); +} + +static void ext4_unregister_li_request(struct super_block *sb) +{ + struct ext4_li_request *elr = EXT4_SB(sb)->s_li_request; + + if (!ext4_li_info) + return; + + mutex_lock(&ext4_li_info->li_list_mtx); + ext4_remove_li_request(elr); + mutex_unlock(&ext4_li_info->li_list_mtx); +} + +/* + * This is the function where ext4lazyinit thread lives. It walks + * through the request list searching for next scheduled filesystem. + * When such a fs is found, run the lazy initialization request + * (ext4_rn_li_request) and keep track of the time spend in this + * function. Based on that time we compute next schedule time of + * the request. When walking through the list is complete, compute + * next waking time and put itself into sleep. + */ +static int ext4_lazyinit_thread(void *arg) +{ + struct ext4_lazy_init *eli = (struct ext4_lazy_init *)arg; + struct list_head *pos, *n; + struct ext4_li_request *elr; + unsigned long next_wakeup; + DEFINE_WAIT(wait); + int ret; + + BUG_ON(NULL == eli); + + eli->li_timer.data = (unsigned long)current; + eli->li_timer.function = ext4_lazyinode_timeout; + + eli->li_task = current; + wake_up(&eli->li_wait_task); + +cont_thread: + while (true) { + next_wakeup = MAX_JIFFY_OFFSET; + + mutex_lock(&eli->li_list_mtx); + if (list_empty(&eli->li_request_list)) { + mutex_unlock(&eli->li_list_mtx); + goto exit_thread; + } + + list_for_each_safe(pos, n, &eli->li_request_list) { + elr = list_entry(pos, struct ext4_li_request, + lr_request); + + if (time_after_eq(jiffies, elr->lr_next_sched)) + ret = ext4_run_li_request(elr); + + if (ret) { + ret = 0; + ext4_remove_li_request(elr); + continue; + } + + if (time_before(elr->lr_next_sched, next_wakeup)) + next_wakeup = elr->lr_next_sched; + } + mutex_unlock(&eli->li_list_mtx); + + if (freezing(current)) + refrigerator(); + + if (time_after_eq(jiffies, next_wakeup)) { + cond_resched(); + continue; + } + + eli->li_timer.expires = next_wakeup; + add_timer(&eli->li_timer); + prepare_to_wait(&eli->li_wait_daemon, &wait, + TASK_INTERRUPTIBLE); + if (time_before(jiffies, next_wakeup)) + schedule(); + finish_wait(&eli->li_wait_daemon, &wait); + } + +exit_thread: + /* + * It looks like the request list is empty, but we need + * to check it under the li_list_mtx lock, to prevent any + * additions into it, and of course we should lock ext4_li_mtx + * to atomically free the list and ext4_li_info, because at + * this point another ext4 filesystem could be registering + * new one. + */ + mutex_lock(&ext4_li_mtx); + mutex_lock(&eli->li_list_mtx); + if (!list_empty(&eli->li_request_list)) { + mutex_unlock(&eli->li_list_mtx); + mutex_unlock(&ext4_li_mtx); + goto cont_thread; + } + mutex_unlock(&eli->li_list_mtx); + del_timer_sync(&ext4_li_info->li_timer); + eli->li_task = NULL; + wake_up(&eli->li_wait_task); + + kfree(ext4_li_info); + ext4_li_info = NULL; + mutex_unlock(&ext4_li_mtx); + + return 0; +} + +static void ext4_clear_request_list(void) +{ + struct list_head *pos, *n; + struct ext4_li_request *elr; + + mutex_lock(&ext4_li_info->li_list_mtx); + if (list_empty(&ext4_li_info->li_request_list)) + return; + + list_for_each_safe(pos, n, &ext4_li_info->li_request_list) { + elr = list_entry(pos, struct ext4_li_request, + lr_request); + ext4_remove_li_request(elr); + } + mutex_unlock(&ext4_li_info->li_list_mtx); +} + +static int ext4_run_lazyinit_thread(void) +{ + struct task_struct *t; + + t = kthread_run(ext4_lazyinit_thread, ext4_li_info, "ext4lazyinit"); + if (IS_ERR(t)) { + int err = PTR_ERR(t); + ext4_clear_request_list(); + del_timer_sync(&ext4_li_info->li_timer); + kfree(ext4_li_info); + ext4_li_info = NULL; + printk(KERN_CRIT "EXT4: error %d creating inode table " + "initialization thread\n", + err); + return err; + } + ext4_li_info->li_state |= EXT4_LAZYINIT_RUNNING; + + wait_event(ext4_li_info->li_wait_task, ext4_li_info->li_task != NULL); + return 0; +} + +/* + * Check whether it make sense to run itable init. thread or not. + * If there is at least one uninitialized inode table, return + * corresponding group number, else the loop goes through all + * groups and return total number of groups. + */ +static ext4_group_t ext4_has_uninit_itable(struct super_block *sb) +{ + ext4_group_t group, ngroups = EXT4_SB(sb)->s_groups_count; + struct ext4_group_desc *gdp = NULL; + + for (group = 0; group < ngroups; group++) { + gdp = ext4_get_group_desc(sb, group, NULL); + if (!gdp) + continue; + + if (!(gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_ZEROED))) + break; + } + + return group; +} + +static int ext4_li_info_new(void) +{ + struct ext4_lazy_init *eli = NULL; + + eli = kzalloc(sizeof(*eli), GFP_KERNEL); + if (!eli) + return -ENOMEM; + + eli->li_task = NULL; + INIT_LIST_HEAD(&eli->li_request_list); + mutex_init(&eli->li_list_mtx); + + init_waitqueue_head(&eli->li_wait_daemon); + init_waitqueue_head(&eli->li_wait_task); + init_timer(&eli->li_timer); + eli->li_state |= EXT4_LAZYINIT_QUIT; + + ext4_li_info = eli; + + return 0; +} + +static struct ext4_li_request *ext4_li_request_new(struct super_block *sb, + ext4_group_t start) +{ + struct ext4_sb_info *sbi = EXT4_SB(sb); + struct ext4_li_request *elr; + unsigned long rnd; + + elr = kzalloc(sizeof(*elr), GFP_KERNEL); + if (!elr) + return NULL; + + elr->lr_super = sb; + elr->lr_sbi = sbi; + elr->lr_next_group = start; + + /* + * Randomize first schedule time of the request to + * spread the inode table initialization requests + * better. + */ + get_random_bytes(&rnd, sizeof(rnd)); + elr->lr_next_sched = jiffies + (unsigned long)rnd % + (EXT4_DEF_LI_MAX_START_DELAY * HZ); + + return elr; +} + +static int ext4_register_li_request(struct super_block *sb, + ext4_group_t first_not_zeroed) +{ + struct ext4_sb_info *sbi = EXT4_SB(sb); + struct ext4_li_request *elr; + ext4_group_t ngroups = EXT4_SB(sb)->s_groups_count; + int ret = 0; + + if (sbi->s_li_request != NULL) + goto out; + + if (first_not_zeroed == ngroups || + (sb->s_flags & MS_RDONLY) || + !test_opt(sb, INIT_INODE_TABLE)) { + sbi->s_li_request = NULL; + goto out; + } + + if (first_not_zeroed == ngroups) { + sbi->s_li_request = NULL; + goto out; + } + + elr = ext4_li_request_new(sb, first_not_zeroed); + if (!elr) { + ret = -ENOMEM; + goto out; + } + + mutex_lock(&ext4_li_mtx); + + if (NULL == ext4_li_info) { + ret = ext4_li_info_new(); + if (ret) + goto out; + } + + mutex_lock(&ext4_li_info->li_list_mtx); + list_add(&elr->lr_request, &ext4_li_info->li_request_list); + mutex_unlock(&ext4_li_info->li_list_mtx); + + sbi->s_li_request = elr; + + if (!(ext4_li_info->li_state & EXT4_LAZYINIT_RUNNING)) { + ret = ext4_run_lazyinit_thread(); + if (ret) + goto out; + } + + mutex_unlock(&ext4_li_mtx); + +out: + if (ret) { + mutex_unlock(&ext4_li_mtx); + kfree(elr); + } + return ret; +} + +/* + * We do not need to lock anything since this is called on + * module unload. + */ +static void ext4_destroy_lazyinit_thread(void) +{ + /* + * If thread exited earlier + * there's nothing to be done. + */ + if (!ext4_li_info) + return; + + ext4_clear_request_list(); + + while (ext4_li_info->li_task) { + wake_up(&ext4_li_info->li_wait_daemon); + wait_event(ext4_li_info->li_wait_task, + ext4_li_info->li_task == NULL); + } +} + static int ext4_fill_super(struct super_block *sb, void *data, int silent) __releases(kernel_lock) __acquires(kernel_lock) @@ -2568,6 +2979,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) __u64 blocks_count; int err; unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO; + ext4_group_t first_not_zeroed; sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) @@ -2630,6 +3042,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) /* Set defaults before we parse the mount options */ def_mount_opts = le32_to_cpu(es->s_default_mount_opts); + set_opt(sbi->s_mount_opt, INIT_INODE_TABLE); if (def_mount_opts & EXT4_DEFM_DEBUG) set_opt(sbi->s_mount_opt, DEBUG); if (def_mount_opts & EXT4_DEFM_BSDGROUPS) { @@ -2909,7 +3322,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount2; } } - if (!ext4_check_descriptors(sb)) { + if (!ext4_check_descriptors(sb, &first_not_zeroed)) { ext4_msg(sb, KERN_ERR, "group descriptors corrupted!"); goto failed_mount2; } @@ -3130,6 +3543,10 @@ no_journal: goto failed_mount4; } + err = ext4_register_li_request(sb, first_not_zeroed); + if (err) + goto failed_mount4; + sbi->s_kobj.kset = ext4_kset; init_completion(&sbi->s_kobj_unregister); err = kobject_init_and_add(&sbi->s_kobj, &ext4_ktype, NULL, @@ -3847,6 +4264,19 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) enable_quota = 1; } } + + /* + * Reinitialize lazy itable initialization thread based on + * current settings + */ + if ((sb->s_flags & MS_RDONLY) || !test_opt(sb, INIT_INODE_TABLE)) + ext4_unregister_li_request(sb); + else { + ext4_group_t first_not_zeroed; + first_not_zeroed = ext4_has_uninit_itable(sb); + ext4_register_li_request(sb, first_not_zeroed); + } + ext4_setup_system_zone(sb); if (sbi->s_journal == NULL) ext4_commit_super(sb, 1); @@ -4317,6 +4747,9 @@ static int __init init_ext4_fs(void) err = register_filesystem(&ext4_fs_type); if (err) goto out; + + ext4_li_info = NULL; + mutex_init(&ext4_li_mtx); return 0; out: unregister_as_ext2(); @@ -4336,6 +4769,7 @@ out4: static void __exit exit_ext4_fs(void) { + ext4_destroy_lazyinit_thread(); unregister_as_ext2(); unregister_as_ext3(); unregister_filesystem(&ext4_fs_type); -- cgit v1.2.3-70-g09d2 From 76381a42e4a5606774fd48413e6282cd7130ff2c Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 28 Sep 2010 00:27:41 +0530 Subject: fs/9p: Add access = client option to opt in acl evaluation. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Venkateswararao Jujjuri Signed-off-by: Eric Van Hensbergen --- Documentation/filesystems/9p.txt | 4 ++- fs/9p/acl.c | 78 +++++++++++++++++++++++++++++++++++++++- fs/9p/fid.c | 1 + fs/9p/v9fs.c | 22 +++++++++++- fs/9p/v9fs.h | 8 +++-- fs/9p/vfs_super.c | 4 +-- 6 files changed, 110 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/9p.txt b/Documentation/filesystems/9p.txt index f9765e8cf086..b22abba78fed 100644 --- a/Documentation/filesystems/9p.txt +++ b/Documentation/filesystems/9p.txt @@ -111,7 +111,7 @@ OPTIONS This can be used to share devices/named pipes/sockets between hosts. This functionality will be expanded in later versions. - access there are three access modes. + access there are four access modes. user = if a user tries to access a file on v9fs filesystem for the first time, v9fs sends an attach command (Tattach) for that user. @@ -120,6 +120,8 @@ OPTIONS the files on the mounted filesystem any = v9fs does single attach and performs all operations as one user + client = ACL based access check on the 9p client + side for access validation cachetag cache tag to use the specified persistent cache. cache tags for existing cache sessions can be listed at diff --git a/fs/9p/acl.c b/fs/9p/acl.c index 8b3c54a4958c..12d602351dbe 100644 --- a/fs/9p/acl.c +++ b/fs/9p/acl.c @@ -22,6 +22,7 @@ #include "xattr.h" #include "acl.h" #include "v9fs_vfs.h" +#include "v9fs.h" static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, char *name) { @@ -55,7 +56,14 @@ int v9fs_get_acl(struct inode *inode, struct p9_fid *fid) { int retval = 0; struct posix_acl *pacl, *dacl; + struct v9fs_session_info *v9ses; + v9ses = v9fs_inode2v9ses(inode); + if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) { + set_cached_acl(inode, ACL_TYPE_DEFAULT, NULL); + set_cached_acl(inode, ACL_TYPE_ACCESS, NULL); + return 0; + } /* get the default/access acl values and cache them */ dacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_DEFAULT); pacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_ACCESS); @@ -85,7 +93,18 @@ static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type) int v9fs_check_acl(struct inode *inode, int mask) { - struct posix_acl *acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS); + struct posix_acl *acl; + struct v9fs_session_info *v9ses; + + v9ses = v9fs_inode2v9ses(inode); + if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) { + /* + * On access = client mode get the acl + * values from the server + */ + return 0; + } + acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS); if (IS_ERR(acl)) return PTR_ERR(acl); @@ -204,15 +223,41 @@ cleanup: } +static int v9fs_remote_get_acl(struct dentry *dentry, const char *name, + void *buffer, size_t size, int type) +{ + char *full_name; + + switch (type) { + case ACL_TYPE_ACCESS: + full_name = POSIX_ACL_XATTR_ACCESS; + break; + case ACL_TYPE_DEFAULT: + full_name = POSIX_ACL_XATTR_DEFAULT; + break; + default: + BUG(); + } + return v9fs_xattr_get(dentry, full_name, buffer, size); +} + static int v9fs_xattr_get_acl(struct dentry *dentry, const char *name, void *buffer, size_t size, int type) { + struct v9fs_session_info *v9ses; struct posix_acl *acl; int error; if (strcmp(name, "") != 0) return -EINVAL; + v9ses = v9fs_inode2v9ses(dentry->d_inode); + /* + * We allow set/get/list of acl when access=client is not specified + */ + if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) + return v9fs_remote_get_acl(dentry, name, buffer, size, type); + acl = v9fs_get_cached_acl(dentry->d_inode, type); if (IS_ERR(acl)) return PTR_ERR(acl); @@ -224,16 +269,47 @@ static int v9fs_xattr_get_acl(struct dentry *dentry, const char *name, return error; } +static int v9fs_remote_set_acl(struct dentry *dentry, const char *name, + const void *value, size_t size, + int flags, int type) +{ + char *full_name; + + switch (type) { + case ACL_TYPE_ACCESS: + full_name = POSIX_ACL_XATTR_ACCESS; + break; + case ACL_TYPE_DEFAULT: + full_name = POSIX_ACL_XATTR_DEFAULT; + break; + default: + BUG(); + } + return v9fs_xattr_set(dentry, full_name, value, size, flags); +} + + static int v9fs_xattr_set_acl(struct dentry *dentry, const char *name, const void *value, size_t size, int flags, int type) { int retval; struct posix_acl *acl; + struct v9fs_session_info *v9ses; struct inode *inode = dentry->d_inode; if (strcmp(name, "") != 0) return -EINVAL; + + v9ses = v9fs_inode2v9ses(dentry->d_inode); + /* + * set the attribute on the remote. Without even looking at the + * xattr value. We leave it to the server to validate + */ + if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) + return v9fs_remote_set_acl(dentry, name, + value, size, flags, type); + if (S_ISLNK(inode->i_mode)) return -EOPNOTSUPP; if (!is_owner_or_cap(inode)) diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 6406f896bf95..b00223c99d70 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -149,6 +149,7 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) switch (access) { case V9FS_ACCESS_SINGLE: case V9FS_ACCESS_USER: + case V9FS_ACCESS_CLIENT: uid = current_fsuid(); any = 0; break; diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 38dc0e067599..2f77cd33ba83 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -193,7 +193,17 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) v9ses->flags |= V9FS_ACCESS_USER; else if (strcmp(s, "any") == 0) v9ses->flags |= V9FS_ACCESS_ANY; - else { + else if (strcmp(s, "client") == 0) { +#ifdef CONFIG_9P_FS_POSIX_ACL + v9ses->flags |= V9FS_ACCESS_CLIENT; +#else + P9_DPRINTK(P9_DEBUG_ERROR, + "access=client option not supported\n"); + kfree(s); + ret = -EINVAL; + goto free_and_return; +#endif + } else { v9ses->flags |= V9FS_ACCESS_SINGLE; v9ses->uid = simple_strtoul(s, &e, 10); if (*e != '\0') @@ -278,6 +288,16 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ; + if (!v9fs_proto_dotl(v9ses) && + ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) { + /* + * We support ACCESS_CLIENT only for dotl. + * Fall back to ACCESS_USER + */ + v9ses->flags &= ~V9FS_ACCESS_MASK; + v9ses->flags |= V9FS_ACCESS_USER; + } + /*FIXME !! */ /* for legacy mode, fall back to V9FS_ACCESS_ANY */ if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) && ((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) { diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index 4c963c9fc41f..8bb7792afe2e 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -33,13 +33,17 @@ * * Session flags reflect options selected by users at mount time */ +#define V9FS_ACCESS_ANY (V9FS_ACCESS_SINGLE | \ + V9FS_ACCESS_USER | \ + V9FS_ACCESS_CLIENT) +#define V9FS_ACCESS_MASK V9FS_ACCESS_ANY + enum p9_session_flags { V9FS_PROTO_2000U = 0x01, V9FS_PROTO_2000L = 0x02, V9FS_ACCESS_SINGLE = 0x04, V9FS_ACCESS_USER = 0x08, - V9FS_ACCESS_ANY = 0x0C, - V9FS_ACCESS_MASK = 0x0C, + V9FS_ACCESS_CLIENT = 0x10 }; /* possible values of ->cache */ diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 14da5778d44e..174643f4f901 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -90,7 +90,8 @@ v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses, MS_NOATIME; #ifdef CONFIG_9P_FS_POSIX_ACL - sb->s_flags |= MS_POSIXACL; + if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT) + sb->s_flags |= MS_POSIXACL; #endif save_mount_options(sb, data); @@ -181,7 +182,6 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags, retval = v9fs_get_acl(inode, fid); if (retval) goto release_sb; - v9fs_fid_add(root, fid); P9_DPRINTK(P9_DEBUG_VFS, " simple set mount, return 0\n"); -- cgit v1.2.3-70-g09d2 From 28e7438faacbd47aaf3e3b43c3074c64be98f5be Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:42 +0200 Subject: hwmon: Add tempX_emergency attribute to sysfs ABI Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- Documentation/hwmon/sysfs-interface | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'Documentation') diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index 48ceabedf55d..645699010551 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -309,6 +309,20 @@ temp[1-*]_crit_hyst from the critical value. RW +temp[1-*]_emergency + Temperature emergency max value, for chips supporting more than + two upper temperature limits. Must be equal or greater than + corresponding temp_crit values. + Unit: millidegree Celsius + RW + +temp[1-*]_emergency_hyst + Temperature hysteresis value for emergency limit. + Unit: millidegree Celsius + Must be reported as an absolute temperature, NOT a delta + from the emergency value. + RW + temp[1-*]_lcrit Temperature critical min value, typically lower than corresponding temp_min values. Unit: millidegree Celsius @@ -505,6 +519,7 @@ fan[1-*]_max_alarm temp[1-*]_min_alarm temp[1-*]_max_alarm temp[1-*]_crit_alarm +temp[1-*]_emergency_alarm Limit alarm 0: no alarm 1: alarm -- cgit v1.2.3-70-g09d2 From 13c84951a3d75ba820adf47eb2a3b1c5ab1fa635 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:43 +0200 Subject: hwmon: (lm90) Add explicit support for max6659 Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- Documentation/hwmon/lm90 | 4 ++-- drivers/hwmon/lm90.c | 32 +++++++++++++++++++++----------- 2 files changed, 23 insertions(+), 13 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index 6a03dd4bcc94..d5ce9f4ddd66 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -63,8 +63,8 @@ Supported chips: Datasheet: Publicly available at the Maxim website http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 * Maxim MAX6659 - Prefix: 'max6657' - Addresses scanned: I2C 0x4c, 0x4d (unsupported 0x4e) + Prefix: 'max6659' + Addresses scanned: I2C 0x4c, 0x4d, 0x4e Datasheet: Publicly available at the Maxim website http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 * Maxim MAX6680 diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index e7ef9657ae17..68ee8f78843e 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -85,7 +85,7 @@ * and MAX6658 have address 0x4c. * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d. * MAX6647 has address 0x4e. - * MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported). + * MAX6659 can have address 0x4c, 0x4d or 0x4e. * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, * 0x4c, 0x4d or 0x4e. */ @@ -93,8 +93,8 @@ static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; -enum chips { lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646, - w83l771 }; +enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, + max6646, w83l771 }; /* * The LM90 registers @@ -176,7 +176,7 @@ static const struct i2c_device_id lm90_id[] = { { "max6649", max6646 }, { "max6657", max6657 }, { "max6658", max6657 }, - { "max6659", max6657 }, + { "max6659", max6659 }, { "max6680", max6680 }, { "max6681", max6680 }, { "w83l771", w83l771 }, @@ -220,7 +220,7 @@ struct lm90_data { s16 temp11[5]; /* 0: remote input 1: remote low limit 2: remote high limit - 3: remote offset (except max6646 and max6657) + 3: remote offset (except max6646 and max6657/58/59) 4: local input */ u8 temp_hyst; u8 alarms; /* bitvector */ @@ -766,12 +766,20 @@ static int lm90_detect(struct i2c_client *new_client, * register. Likewise, the config1 register seems to lack a * low nibble, so the value will be those of the previous * read, so in our case those of the man_id register. + * MAX6659 has a third set of upper temperature limit registers. + * Those registers also return values on MAX6657 and MAX6658, + * thus the only way to detect MAX6659 is by its address. + * For this reason it will be mis-detected as MAX6657 if its + * address is 0x4C. */ if (chip_id == man_id - && (address == 0x4C || address == 0x4D) + && (address == 0x4C || address == 0x4D || address == 0x4E) && (reg_config1 & 0x1F) == (man_id & 0x0F) && reg_convrate <= 0x09) { - name = "max6657"; + if (address == 0x4C) + name = "max6657"; + else + name = "max6659"; } else /* * The chip_id register of the MAX6680 and MAX6681 holds the @@ -861,14 +869,16 @@ static int lm90_probe(struct i2c_client *new_client, } /* Set chip capabilities */ - if (data->kind != max6657 && data->kind != max6646) + if (data->kind != max6657 && data->kind != max6659 + && data->kind != max6646) data->flags |= LM90_HAVE_OFFSET; - if (data->kind == max6657 || data->kind == max6646) + if (data->kind == max6657 || data->kind == max6659 + || data->kind == max6646) data->flags |= LM90_HAVE_LOCAL_EXT; - if (data->kind != max6657 && data->kind != max6646 - && data->kind != max6680) + if (data->kind != max6657 && data->kind != max6659 + && data->kind != max6646 && data->kind != max6680) data->flags |= LM90_HAVE_REM_LIMIT_EXT; /* Initialize the LM90 chip */ -- cgit v1.2.3-70-g09d2 From 6948708dd07573c578aa99f80915cd1867334abe Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:43 +0200 Subject: hwmon: (lm90) Add support for extra features of max6659 Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- Documentation/hwmon/lm90 | 9 ++++--- drivers/hwmon/lm90.c | 65 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 64 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index d5ce9f4ddd66..bc2c2b4e0d53 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -101,10 +101,11 @@ well as the temperature of up to one external diode. It is compatible with many other devices, many of which are supported by this driver. Note that there is no easy way to differentiate between the MAX6657, -MAX6658 and MAX6659 variants. The extra address and features of the -MAX6659 are not supported by this driver. The MAX6680 and MAX6681 only -differ in their pinout, therefore they obviously can't (and don't need to) -be distinguished. +MAX6658 and MAX6659 variants. The extra features of the MAX6659 are only +supported by this driver if the chip is located at address 0x4d or 0x4e, +or if the chip type is explicitly selected as max6659. +The MAX6680 and MAX6681 only differ in their pinout, therefore they obviously +can't (and don't need to) be distinguished. The specificity of this family of chipsets over the ADM1021/LM84 family is that it features critical limits with hysteresis, and an diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 68ee8f78843e..de544817d673 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -28,9 +28,11 @@ * This driver also supports the MAX6657, MAX6658 and MAX6659 sensor * chips made by Maxim. These chips are similar to the LM86. * Note that there is no easy way to differentiate between the three - * variants. The extra address and features of the MAX6659 are not - * supported by this driver. These chips lack the remote temperature - * offset feature. + * variants. We use the device address to detect MAX6659, which will result + * in a detection as max6657 if it is on address 0x4c. The extra address + * and features of the MAX6659 are only supported if the chip is configured + * explicitly as max6659, or if its address is not 0x4c. + * These chips lack the remote temperature offset feature. * * This driver also supports the MAX6646, MAX6647, MAX6648, MAX6649 and * MAX6692 chips made by Maxim. These are again similar to the LM86, @@ -138,6 +140,10 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, /* MAX6646/6647/6649/6657/6658/6659 registers */ #define MAX6657_REG_R_LOCAL_TEMPL 0x11 +#define MAX6659_REG_R_REMOTE_EMERG 0x16 +#define MAX6659_REG_W_REMOTE_EMERG 0x16 +#define MAX6659_REG_R_LOCAL_EMERG 0x17 +#define MAX6659_REG_W_LOCAL_EMERG 0x17 /* * Device flags @@ -147,6 +153,7 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_HAVE_OFFSET (1 << 1) /* temperature offset register */ #define LM90_HAVE_LOCAL_EXT (1 << 2) /* extended local temperature */ #define LM90_HAVE_REM_LIMIT_EXT (1 << 3) /* extended remote limit */ +#define LM90_HAVE_EMERGENCY (1 << 4) /* 3rd upper (emergency) limit */ /* * Functions declaration @@ -213,10 +220,12 @@ struct lm90_data { u8 alert_alarms; /* Which alarm bits trigger ALERT# */ /* registers values */ - s8 temp8[4]; /* 0: local low limit + s8 temp8[6]; /* 0: local low limit 1: local high limit 2: local critical limit - 3: remote critical limit */ + 3: remote critical limit + 4: local emergency limit (max6659 only) + 5: remote emergency limit (max6659 only) */ s16 temp11[5]; /* 0: remote input 1: remote low limit 2: remote high limit @@ -381,11 +390,13 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - static const u8 reg[4] = { + static const u8 reg[6] = { LM90_REG_W_LOCAL_LOW, LM90_REG_W_LOCAL_HIGH, LM90_REG_W_LOCAL_CRIT, LM90_REG_W_REMOTE_CRIT, + MAX6659_REG_W_LOCAL_EMERG, + MAX6659_REG_W_REMOTE_EMERG, }; struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); @@ -608,6 +619,30 @@ static const struct attribute_group lm90_group = { .attrs = lm90_attributes, }; +/* + * Additional attributes for devices with emergency sensors + */ +static SENSOR_DEVICE_ATTR(temp1_emergency, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 4); +static SENSOR_DEVICE_ATTR(temp2_emergency, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 5); +static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO, show_temphyst, + NULL, 4); +static SENSOR_DEVICE_ATTR(temp2_emergency_hyst, S_IRUGO, show_temphyst, + NULL, 5); + +static struct attribute *lm90_emergency_attributes[] = { + &sensor_dev_attr_temp1_emergency.dev_attr.attr, + &sensor_dev_attr_temp2_emergency.dev_attr.attr, + &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_emergency_hyst.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm90_emergency_group = { + .attrs = lm90_emergency_attributes, +}; + /* pec used for ADM1032 only */ static ssize_t show_pec(struct device *dev, struct device_attribute *dummy, char *buf) @@ -826,6 +861,9 @@ static int lm90_detect(struct i2c_client *new_client, static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data) { + if (data->flags & LM90_HAVE_EMERGENCY) + sysfs_remove_group(&client->dev.kobj, + &lm90_emergency_group); if (data->flags & LM90_HAVE_OFFSET) device_remove_file(&client->dev, &sensor_dev_attr_temp2_offset.dev_attr); @@ -881,6 +919,9 @@ static int lm90_probe(struct i2c_client *new_client, && data->kind != max6646 && data->kind != max6680) data->flags |= LM90_HAVE_REM_LIMIT_EXT; + if (data->kind == max6659) + data->flags |= LM90_HAVE_EMERGENCY; + /* Initialize the LM90 chip */ lm90_init_client(new_client); @@ -899,6 +940,12 @@ static int lm90_probe(struct i2c_client *new_client, if (err) goto exit_remove_files; } + if (data->flags & LM90_HAVE_EMERGENCY) { + err = sysfs_create_group(&new_client->dev.kobj, + &lm90_emergency_group); + if (err) + goto exit_remove_files; + } data->hwmon_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -1082,6 +1129,12 @@ static struct lm90_data *lm90_update_device(struct device *dev) &l) == 0) data->temp11[3] = (h << 8) | l; } + if (data->flags & LM90_HAVE_EMERGENCY) { + lm90_read_reg(client, MAX6659_REG_R_LOCAL_EMERG, + &data->temp8[4]); + lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, + &data->temp8[5]); + } lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms); /* Re-enable ALERT# output if it was originally enabled and -- cgit v1.2.3-70-g09d2 From 06e1c0a2167d48442d0bd06373390886670aa6e5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 28 Oct 2010 20:31:43 +0200 Subject: hwmon: (lm90) Add support for max6695 and max6696 Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- Documentation/hwmon/lm90 | 17 ++++ drivers/hwmon/Kconfig | 4 +- drivers/hwmon/lm90.c | 250 ++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 247 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index bc2c2b4e0d53..6e963b696d85 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -84,6 +84,17 @@ Supported chips: Addresses scanned: I2C 0x4c Datasheet: Publicly available at the Maxim website http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3500 + * Maxim MAX6695 + Prefix: 'max6695' + Addresses scanned: I2C 0x18 + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/datasheet/index.mvp/id/4199 + * Maxim MAX6696 + Prefix: 'max6695' + Addresses scanned: I2C 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, + 0x4c, 0x4d and 0x4e + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/datasheet/index.mvp/id/4199 * Winbond/Nuvoton W83L771AWG/ASG Prefix: 'w83l771' Addresses scanned: I2C 0x4c @@ -152,6 +163,12 @@ MAX6680 and MAX6681: * Selectable address * Remote sensor type selection +MAX6695 and MAX6696: + * Better local resolution + * Selectable address (max6696) + * Second critical temperature limit + * Two remote sensors + W83L771AWG/ASG * The AWG and ASG variants only differ in package format. * Filter and alert configuration register at 0xBF diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index c357c835eb1e..9a3742b67c8c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -614,8 +614,8 @@ config SENSORS_LM90 If you say yes here you get support for National Semiconductor LM90, LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, - MAX6680, MAX6681 and MAX6692, and Winbond/Nuvoton W83L771AWG/ASG - sensor chips. + MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, and Winbond/Nuvoton + W83L771AWG/ASG sensor chips. This driver can also be built as a module. If so, the module will be called lm90. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index de544817d673..366bb624e655 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -44,6 +44,11 @@ * chips. The MAX6680 and MAX6681 only differ in the pinout so they can * be treated identically. * + * This driver also supports the MAX6695 and MAX6696, two other sensor + * chips made by Maxim. These are also quite similar to other Maxim + * chips, but support three temperature sensors instead of two. MAX6695 + * and MAX6696 only differ in the pinout so they can be treated identically. + * * This driver also supports the ADT7461 chip from Analog Devices. * It's supported in both compatibility and extended mode. It is mostly * compatible with LM90 except for a data format difference for the @@ -96,7 +101,7 @@ static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, - max6646, w83l771 }; + max6646, w83l771, max6696 }; /* * The LM90 registers @@ -137,9 +142,10 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_REG_R_TCRIT_HYST 0x21 #define LM90_REG_W_TCRIT_HYST 0x21 -/* MAX6646/6647/6649/6657/6658/6659 registers */ +/* MAX6646/6647/6649/6657/6658/6659/6695/6696 registers */ #define MAX6657_REG_R_LOCAL_TEMPL 0x11 +#define MAX6696_REG_R_STATUS2 0x12 #define MAX6659_REG_R_REMOTE_EMERG 0x16 #define MAX6659_REG_W_REMOTE_EMERG 0x16 #define MAX6659_REG_R_LOCAL_EMERG 0x17 @@ -154,6 +160,8 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_HAVE_LOCAL_EXT (1 << 2) /* extended local temperature */ #define LM90_HAVE_REM_LIMIT_EXT (1 << 3) /* extended remote limit */ #define LM90_HAVE_EMERGENCY (1 << 4) /* 3rd upper (emergency) limit */ +#define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm */ +#define LM90_HAVE_TEMP3 (1 << 6) /* 3rd temperature sensor */ /* * Functions declaration @@ -166,6 +174,9 @@ static void lm90_init_client(struct i2c_client *client); static void lm90_alert(struct i2c_client *client, unsigned int flag); static int lm90_remove(struct i2c_client *client); static struct lm90_data *lm90_update_device(struct device *dev); +static inline void lm90_select_remote_channel(struct i2c_client *client, + struct lm90_data *data, + int channel); /* * Driver data (common to all clients) @@ -186,6 +197,8 @@ static const struct i2c_device_id lm90_id[] = { { "max6659", max6659 }, { "max6680", max6680 }, { "max6681", max6680 }, + { "max6695", max6696 }, + { "max6696", max6696 }, { "w83l771", w83l771 }, { } }; @@ -217,22 +230,29 @@ struct lm90_data { int flags; u8 config_orig; /* Original configuration register value */ - u8 alert_alarms; /* Which alarm bits trigger ALERT# */ + u16 alert_alarms; /* Which alarm bits trigger ALERT# */ + /* Upper 8 bits for max6695/96 */ /* registers values */ - s8 temp8[6]; /* 0: local low limit + s8 temp8[8]; /* 0: local low limit 1: local high limit 2: local critical limit 3: remote critical limit - 4: local emergency limit (max6659 only) - 5: remote emergency limit (max6659 only) */ - s16 temp11[5]; /* 0: remote input + 4: local emergency limit (max6659 and max6695/96) + 5: remote emergency limit (max6659 and max6695/96) + 6: remote 2 critical limit (max6695/96 only) + 7: remote 2 emergency limit (max6695/96 only) */ + s16 temp11[8]; /* 0: remote input 1: remote low limit 2: remote high limit - 3: remote offset (except max6646 and max6657/58/59) - 4: local input */ + 3: remote offset (except max6646, max6657/58/59, + and max6695/96) + 4: local input + 5: remote 2 input (max6695/96 only) + 6: remote 2 low limit (max6695/96 only) + 7: remote 2 high limit (ma6695/96 only) */ u8 temp_hyst; - u8 alarms; /* bitvector */ + u16 alarms; /* bitvector (upper 8 bits for max6695/96) */ }; /* @@ -390,13 +410,15 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - static const u8 reg[6] = { + static const u8 reg[8] = { LM90_REG_W_LOCAL_LOW, LM90_REG_W_LOCAL_HIGH, LM90_REG_W_LOCAL_CRIT, LM90_REG_W_REMOTE_CRIT, MAX6659_REG_W_LOCAL_EMERG, MAX6659_REG_W_REMOTE_EMERG, + LM90_REG_W_REMOTE_CRIT, + MAX6659_REG_W_REMOTE_EMERG, }; struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); @@ -421,7 +443,11 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, data->temp8[nr] = temp_to_u8(val); else data->temp8[nr] = temp_to_s8(val); + + lm90_select_remote_channel(client, data, nr >= 6); i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); + lm90_select_remote_channel(client, data, 0); + mutex_unlock(&data->update_lock); return count; } @@ -453,10 +479,13 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, struct { u8 high; u8 low; - } reg[3] = { - { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL }, - { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL }, - { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL } + int channel; + } reg[5] = { + { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL, 0 }, + { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL, 0 }, + { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL, 0 }, + { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL, 1 }, + { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL, 1 } }; struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); @@ -485,11 +514,14 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, else data->temp11[index] = temp_to_s8(val) << 8; + lm90_select_remote_channel(client, data, reg[nr].channel); i2c_smbus_write_byte_data(client, reg[nr].high, data->temp11[index] >> 8); if (data->flags & LM90_HAVE_REM_LIMIT_EXT) i2c_smbus_write_byte_data(client, reg[nr].low, data->temp11[index] & 0xff); + lm90_select_remote_channel(client, data, 0); + mutex_unlock(&data->update_lock); return count; } @@ -643,6 +675,62 @@ static const struct attribute_group lm90_emergency_group = { .attrs = lm90_emergency_attributes, }; +static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, show_alarm, NULL, 15); +static SENSOR_DEVICE_ATTR(temp2_emergency_alarm, S_IRUGO, show_alarm, NULL, 13); + +static struct attribute *lm90_emergency_alarm_attributes[] = { + &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_emergency_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm90_emergency_alarm_group = { + .attrs = lm90_emergency_alarm_attributes, +}; + +/* + * Additional attributes for devices with 3 temperature sensors + */ +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp11, NULL, 0, 5); +static SENSOR_DEVICE_ATTR_2(temp3_min, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 3, 6); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 4, 7); +static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 6); +static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temphyst, NULL, 6); +static SENSOR_DEVICE_ATTR(temp3_emergency, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 7); +static SENSOR_DEVICE_ATTR(temp3_emergency_hyst, S_IRUGO, show_temphyst, + NULL, 7); + +static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 9); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 10); +static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 11); +static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 12); +static SENSOR_DEVICE_ATTR(temp3_emergency_alarm, S_IRUGO, show_alarm, NULL, 14); + +static struct attribute *lm90_temp3_attributes[] = { + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_emergency.dev_attr.attr, + &sensor_dev_attr_temp3_emergency_hyst.dev_attr.attr, + + &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_emergency_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm90_temp3_group = { + .attrs = lm90_temp3_attributes, +}; + /* pec used for ADM1032 only */ static ssize_t show_pec(struct device *dev, struct device_attribute *dummy, char *buf) @@ -720,6 +808,30 @@ static int lm90_read_reg(struct i2c_client *client, u8 reg, u8 *value) return 0; } +/* + * client->update_lock must be held when calling this function (unless we are + * in detection or initialization steps), and while a remote channel other + * than channel 0 is selected. Also, calling code must make sure to re-select + * external channel 0 before releasing the lock. This is necessary because + * various registers have different meanings as a result of selecting a + * non-default remote channel. + */ +static inline void lm90_select_remote_channel(struct i2c_client *client, + struct lm90_data *data, + int channel) +{ + u8 config; + + if (data->kind == max6696) { + lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); + config &= ~0x08; + if (channel) + config |= 0x08; + i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, + config); + } +} + /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm90_detect(struct i2c_client *new_client, struct i2c_board_info *info) @@ -794,6 +906,23 @@ static int lm90_detect(struct i2c_client *new_client, } } else if (man_id == 0x4D) { /* Maxim */ + int reg_emerg, reg_emerg2, reg_status2; + + /* + * We read MAX6659_REG_R_REMOTE_EMERG twice, and re-read + * LM90_REG_R_MAN_ID in between. If MAX6659_REG_R_REMOTE_EMERG + * exists, both readings will reflect the same value. Otherwise, + * the readings will be different. + */ + if ((reg_emerg = i2c_smbus_read_byte_data(new_client, + MAX6659_REG_R_REMOTE_EMERG)) < 0 + || i2c_smbus_read_byte_data(new_client, LM90_REG_R_MAN_ID) < 0 + || (reg_emerg2 = i2c_smbus_read_byte_data(new_client, + MAX6659_REG_R_REMOTE_EMERG)) < 0 + || (reg_status2 = i2c_smbus_read_byte_data(new_client, + MAX6696_REG_R_STATUS2)) < 0) + return -ENODEV; + /* * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id * register. Reading from that address will return the last @@ -816,6 +945,24 @@ static int lm90_detect(struct i2c_client *new_client, else name = "max6659"; } else + /* + * Even though MAX6695 and MAX6696 do not have a chip ID + * register, reading it returns 0x01. Bit 4 of the config1 + * register is unused and should return zero when read. Bit 0 of + * the status2 register is unused and should return zero when + * read. + * + * MAX6695 and MAX6696 have an additional set of temperature + * limit registers. We can detect those chips by checking if + * one of those registers exists. + */ + if (chip_id == 0x01 + && (reg_config1 & 0x10) == 0x00 + && (reg_status2 & 0x01) == 0x00 + && reg_emerg == reg_emerg2 + && reg_convrate <= 0x07) { + name = "max6696"; + } else /* * The chip_id register of the MAX6680 and MAX6681 holds the * revision of the chip. The lowest bit of the config1 register @@ -861,6 +1008,11 @@ static int lm90_detect(struct i2c_client *new_client, static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data) { + if (data->flags & LM90_HAVE_TEMP3) + sysfs_remove_group(&client->dev.kobj, &lm90_temp3_group); + if (data->flags & LM90_HAVE_EMERGENCY_ALARM) + sysfs_remove_group(&client->dev.kobj, + &lm90_emergency_alarm_group); if (data->flags & LM90_HAVE_EMERGENCY) sysfs_remove_group(&client->dev.kobj, &lm90_emergency_group); @@ -901,6 +1053,9 @@ static int lm90_probe(struct i2c_client *new_client, case lm86: data->alert_alarms = 0x7b; break; + case max6696: + data->alert_alarms = 0x187c; + break; default: data->alert_alarms = 0x7c; break; @@ -908,20 +1063,24 @@ static int lm90_probe(struct i2c_client *new_client, /* Set chip capabilities */ if (data->kind != max6657 && data->kind != max6659 - && data->kind != max6646) + && data->kind != max6646 && data->kind != max6696) data->flags |= LM90_HAVE_OFFSET; if (data->kind == max6657 || data->kind == max6659 - || data->kind == max6646) + || data->kind == max6646 || data->kind == max6696) data->flags |= LM90_HAVE_LOCAL_EXT; if (data->kind != max6657 && data->kind != max6659 - && data->kind != max6646 && data->kind != max6680) + && data->kind != max6646 && data->kind != max6680 + && data->kind != max6696) data->flags |= LM90_HAVE_REM_LIMIT_EXT; - if (data->kind == max6659) + if (data->kind == max6659 || data->kind == max6696) data->flags |= LM90_HAVE_EMERGENCY; + if (data->kind == max6696) + data->flags |= LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3; + /* Initialize the LM90 chip */ lm90_init_client(new_client); @@ -946,6 +1105,18 @@ static int lm90_probe(struct i2c_client *new_client, if (err) goto exit_remove_files; } + if (data->flags & LM90_HAVE_EMERGENCY_ALARM) { + err = sysfs_create_group(&new_client->dev.kobj, + &lm90_emergency_alarm_group); + if (err) + goto exit_remove_files; + } + if (data->flags & LM90_HAVE_TEMP3) { + err = sysfs_create_group(&new_client->dev.kobj, + &lm90_temp3_group); + if (err) + goto exit_remove_files; + } data->hwmon_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -993,6 +1164,12 @@ static void lm90_init_client(struct i2c_client *client) if (data->kind == max6680) config |= 0x18; + /* + * Select external channel 0 for max6695/96 + */ + if (data->kind == max6696) + config &= ~0x08; + config &= 0xBF; /* run */ if (config != data->config_orig) /* Only write if changed */ i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config); @@ -1016,10 +1193,14 @@ static int lm90_remove(struct i2c_client *client) static void lm90_alert(struct i2c_client *client, unsigned int flag) { struct lm90_data *data = i2c_get_clientdata(client); - u8 config, alarms; + u8 config, alarms, alarms2 = 0; lm90_read_reg(client, LM90_REG_R_STATUS, &alarms); - if ((alarms & 0x7f) == 0) { + + if (data->kind == max6696) + lm90_read_reg(client, MAX6696_REG_R_STATUS2, &alarms2); + + if ((alarms & 0x7f) == 0 && (alarms2 & 0xfe) == 0) { dev_info(&client->dev, "Everything OK\n"); } else { if (alarms & 0x61) @@ -1032,6 +1213,10 @@ static void lm90_alert(struct i2c_client *client, unsigned int flag) dev_warn(&client->dev, "temp%d diode open, please check!\n", 2); + if (alarms2 & 0x18) + dev_warn(&client->dev, + "temp%d out of range, please check!\n", 3); + /* Disable ALERT# output, because these chips don't implement SMBus alert correctly; they should only hold the alert line low briefly. */ @@ -1087,6 +1272,7 @@ static struct lm90_data *lm90_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + HZ / 2 + HZ / 10) || !data->valid) { u8 h, l; + u8 alarms; dev_dbg(&client->dev, "Updating lm90 data.\n"); lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]); @@ -1135,7 +1321,27 @@ static struct lm90_data *lm90_update_device(struct device *dev) lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, &data->temp8[5]); } - lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms); + lm90_read_reg(client, LM90_REG_R_STATUS, &alarms); + data->alarms = alarms; /* save as 16 bit value */ + + if (data->kind == max6696) { + lm90_select_remote_channel(client, data, 1); + lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, + &data->temp8[6]); + lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, + &data->temp8[7]); + lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, + LM90_REG_R_REMOTE_TEMPL, &data->temp11[5]); + if (!lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h)) + data->temp11[6] = h << 8; + if (!lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h)) + data->temp11[7] = h << 8; + lm90_select_remote_channel(client, data, 0); + + if (!lm90_read_reg(client, MAX6696_REG_R_STATUS2, + &alarms)) + data->alarms |= alarms << 8; + } /* Re-enable ALERT# output if it was originally enabled and * relevant alarms are all clear */ -- cgit v1.2.3-70-g09d2 From c4f99a2b8fb4c564865f0037a2b7be690d4409f3 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:44 +0200 Subject: hwmon: (lm90) Add support for the W83L771W/G I was wondering if that chip ever existed publicly... Apparently yes, so add support for it. Signed-off-by: Jean Delvare Tested-by: Alexander Stein Acked-by: Guenter Roeck --- Documentation/hwmon/lm90 | 12 ++++++++++-- drivers/hwmon/Kconfig | 2 +- drivers/hwmon/lm90.c | 25 +++++++++++++++++++------ 3 files changed, 30 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index 6e963b696d85..fa475c0a48a3 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -95,6 +95,10 @@ Supported chips: 0x4c, 0x4d and 0x4e Datasheet: Publicly available at the Maxim website http://www.maxim-ic.com/datasheet/index.mvp/id/4199 + * Winbond/Nuvoton W83L771W/G + Prefix: 'w83l771' + Addresses scanned: I2C 0x4c + Datasheet: No longer available * Winbond/Nuvoton W83L771AWG/ASG Prefix: 'w83l771' Addresses scanned: I2C 0x4c @@ -169,11 +173,15 @@ MAX6695 and MAX6696: * Second critical temperature limit * Two remote sensors +W83L771W/G + * The G variant is lead-free, otherwise similar to the W. + * Filter and alert configuration register at 0xBF + * Moving average (depending on conversion rate) + W83L771AWG/ASG + * Successor of the W83L771W/G, same features. * The AWG and ASG variants only differ in package format. - * Filter and alert configuration register at 0xBF * Diode ideality factor configuration (remote sensor) at 0xE3 - * Moving average (depending on conversion rate) All temperature values are given in degrees Celsius. Resolution is 1.0 degree for the local temperature, 0.125 degree for the remote diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 9a3742b67c8c..3e88140b9d77 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -615,7 +615,7 @@ config SENSORS_LM90 LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, and Winbond/Nuvoton - W83L771AWG/ASG sensor chips. + W83L771W/G/AWG/ASG sensor chips. This driver can also be built as a module. If so, the module will be called lm90. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index f9e76c757de8..812781c655a7 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -88,8 +88,8 @@ * Addresses to scan * Address is fully defined internally and cannot be changed except for * MAX6659, MAX6680 and MAX6681. - * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657 - * and MAX6658 have address 0x4c. + * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657, + * MAX6658 and W83L771 have address 0x4c. * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d. * MAX6647 has address 0x4e. * MAX6659 can have address 0x4c, 0x4d or 0x4e. @@ -1237,10 +1237,23 @@ static int lm90_detect(struct i2c_client *new_client, } else if (address == 0x4C && man_id == 0x5C) { /* Winbond/Nuvoton */ - if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */ - && (reg_config1 & 0x2A) == 0x00 - && reg_convrate <= 0x08) { - name = "w83l771"; + int reg_config2; + + reg_config2 = i2c_smbus_read_byte_data(new_client, + LM90_REG_R_CONFIG2); + if (reg_config2 < 0) + return -ENODEV; + + if ((reg_config1 & 0x2A) == 0x00 + && (reg_config2 & 0xF8) == 0x00) { + if (chip_id == 0x01 /* W83L771W/G */ + && reg_convrate <= 0x09) { + name = "w83l771"; + } else + if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */ + && reg_convrate <= 0x08) { + name = "w83l771"; + } } } -- cgit v1.2.3-70-g09d2 From 6dfee85397a47063291fe199eaf950bee7944454 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:50 +0200 Subject: hwmon: (pcf8591) Don't attempt to detect devices The PCF8591 can't be detected, don't even try. There are plenty of other means to instantiate i2c devices these days. Signed-off-by: Jean Delvare Reviewed-by: Guenter Roeck Cc: Aurelien Jarno --- Documentation/hwmon/pcf8591 | 18 ++++++++---------- drivers/hwmon/pcf8591.c | 26 -------------------------- 2 files changed, 8 insertions(+), 36 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/pcf8591 b/Documentation/hwmon/pcf8591 index e76a7892f68e..ac020b3bb7b3 100644 --- a/Documentation/hwmon/pcf8591 +++ b/Documentation/hwmon/pcf8591 @@ -4,7 +4,7 @@ Kernel driver pcf8591 Supported chips: * Philips/NXP PCF8591 Prefix: 'pcf8591' - Addresses scanned: I2C 0x48 - 0x4f + Addresses scanned: none Datasheet: Publicly available at the NXP website http://www.nxp.com/pip/PCF8591_6.html @@ -58,18 +58,16 @@ Module parameters Accessing PCF8591 via /sys interface ------------------------------------- -! Be careful ! -The PCF8591 is plainly impossible to detect! Stupid chip. -So every chip with address in the interval [0x48..0x4f] is -detected as PCF8591. If you have other chips in this address -range, the workaround is to load this module after the one -for your others chips. +The PCF8591 is plainly impossible to detect! Thus the driver won't even +try. You have to explicitly instantiate the device at the relevant +address (in the interval [0x48..0x4f]) either through platform data, or +using the sysfs interface. See Documentation/i2c/instantiating-devices +for details. -On detection (i.e. insmod, modprobe et al.), directories are being -created for each detected PCF8591: +Directories are being created for each instantiated PCF8591: /sys/bus/i2c/devices/<0>-<1>/ -where <0> is the bus the chip was detected on (e. g. i2c-0) +where <0> is the bus the chip is connected to (e. g. i2c-0) and <1> the chip address ([48..4f]) Inside these directories, there are such files: diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c index d7df0e6c1ef4..dc7259d69812 100644 --- a/drivers/hwmon/pcf8591.c +++ b/drivers/hwmon/pcf8591.c @@ -26,10 +26,6 @@ #include #include -/* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, - 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; - /* Insmod parameters */ static int input_mode; @@ -170,24 +166,6 @@ static const struct attribute_group pcf8591_attr_group_opt = { * Real code */ -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int pcf8591_detect(struct i2c_client *client, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE - | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -ENODEV; - - /* Now, we would do the remaining detection. But the PCF8591 is plainly - impossible to detect! Stupid chip. */ - - strlcpy(info->type, "pcf8591", I2C_NAME_SIZE); - - return 0; -} - static int pcf8591_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -307,10 +285,6 @@ static struct i2c_driver pcf8591_driver = { .probe = pcf8591_probe, .remove = pcf8591_remove, .id_table = pcf8591_id, - - .class = I2C_CLASS_HWMON, /* Nearest choice */ - .detect = pcf8591_detect, - .address_list = normal_i2c, }; static int __init pcf8591_init(void) -- cgit v1.2.3-70-g09d2 From c36364dbf38382ef6be2fb99a3ce361a679c0ecb Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:50 +0200 Subject: hwmon: (lm85) Document the ADT7468 as supported Signed-off-by: Jean Delvare Cc: Darrick J. Wong Acked-by: Guenter Roeck --- Documentation/hwmon/lm85 | 53 ++++++++++++++++++++++++++---------------------- drivers/hwmon/Kconfig | 3 ++- 2 files changed, 31 insertions(+), 25 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm85 b/Documentation/hwmon/lm85 index b98e0e0d1910..83cd5bb1ac79 100644 --- a/Documentation/hwmon/lm85 +++ b/Documentation/hwmon/lm85 @@ -14,6 +14,10 @@ Supported chips: Prefix: 'adt7463' Addresses scanned: I2C 0x2c, 0x2d, 0x2e Datasheet: http://www.onsemi.com/PowerSolutions/product.do?id=ADT7463 + * Analog Devices ADT7468 + Prefix: 'adt7468' + Addresses scanned: I2C 0x2c, 0x2d, 0x2e + Datasheet: http://www.onsemi.com/PowerSolutions/product.do?id=ADT7468 * SMSC EMC6D100, SMSC EMC6D101 Prefix: 'emc6d100' Addresses scanned: I2C 0x2c, 0x2d, 0x2e @@ -34,7 +38,7 @@ Description ----------- This driver implements support for the National Semiconductor LM85 and -compatible chips including the Analog Devices ADM1027, ADT7463 and +compatible chips including the Analog Devices ADM1027, ADT7463, ADT7468 and SMSC EMC6D10x chips family. The LM85 uses the 2-wire interface compatible with the SMBUS 2.0 @@ -87,14 +91,15 @@ To smooth the response of fans to changes in temperature, the LM85 has an optional filter for smoothing temperatures. The ADM1027 has the same config option but uses it to rate limit the changes to fan speed instead. -The ADM1027 and ADT7463 have a 10-bit ADC and can therefore measure -temperatures with 0.25 degC resolution. They also provide an offset to the -temperature readings that is automatically applied during measurement. -This offset can be used to zero out any errors due to traces and placement. -The documentation says that the offset is in 0.25 degC steps, but in -initial testing of the ADM1027 it was 1.00 degC steps. Analog Devices has -confirmed this "bug". The ADT7463 is reported to work as described in the -documentation. The current lm85 driver does not show the offset register. +The ADM1027, ADT7463 and ADT7468 have a 10-bit ADC and can therefore +measure temperatures with 0.25 degC resolution. They also provide an offset +to the temperature readings that is automatically applied during +measurement. This offset can be used to zero out any errors due to traces +and placement. The documentation says that the offset is in 0.25 degC +steps, but in initial testing of the ADM1027 it was 1.00 degC steps. Analog +Devices has confirmed this "bug". The ADT7463 is reported to work as +described in the documentation. The current lm85 driver does not show the +offset register. See the vendor datasheets for more information. There is application note from National (AN-1260) with some additional information about the LM85. @@ -125,17 +130,17 @@ datasheet for a complete description of the differences. Other than identifying the chip, the driver behaves no differently with regard to these two chips. The LM85B is recommended for new designs. -The ADM1027 and ADT7463 chips have an optional SMBALERT output that can be -used to signal the chipset in case a limit is exceeded or the temperature -sensors fail. Individual sensor interrupts can be masked so they won't -trigger SMBALERT. The SMBALERT output if configured replaces one of the other -functions (PWM2 or IN0). This functionality is not implemented in current -driver. +The ADM1027, ADT7463 and ADT7468 chips have an optional SMBALERT output +that can be used to signal the chipset in case a limit is exceeded or the +temperature sensors fail. Individual sensor interrupts can be masked so +they won't trigger SMBALERT. The SMBALERT output if configured replaces one +of the other functions (PWM2 or IN0). This functionality is not implemented +in current driver. -The ADT7463 also has an optional THERM output/input which can be connected -to the processor PROC_HOT output. If available, the autofan control -dynamic Tmin feature can be enabled to keep the system temperature within -spec (just?!) with the least possible fan noise. +The ADT7463 and ADT7468 also have an optional THERM output/input which can +be connected to the processor PROC_HOT output. If available, the autofan +control dynamic Tmin feature can be enabled to keep the system temperature +within spec (just?!) with the least possible fan noise. Configuration Notes ------------------- @@ -201,8 +206,8 @@ the temperatures to compensate for systemic errors in the measurements. These features are not currently supported by the lm85 driver. -In addition to the ADM1027 features, the ADT7463 also has Tmin control -and THERM asserted counts. Automatic Tmin control acts to adjust the -Tmin value to maintain the measured temperature sensor at a specified -temperature. There isn't much documentation on this feature in the -ADT7463 data sheet. This is not supported by current driver. +In addition to the ADM1027 features, the ADT7463 and ADT7468 also have +Tmin control and THERM asserted counts. Automatic Tmin control acts to +adjust the Tmin value to maintain the measured temperature sensor at a +specified temperature. There isn't much documentation on this feature in +the ADT7463 data sheet. This is not supported by current driver. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index ad54f6432e2d..ba6905a212bc 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -565,7 +565,8 @@ config SENSORS_LM85 select HWMON_VID help If you say yes here you get support for National Semiconductor LM85 - sensor chips and clones: ADT7463, EMC6D100, EMC6D102 and ADM1027. + sensor chips and clones: ADM1027, ADT7463, ADT7468, EMC6D100, + EMC6D101 and EMC6D102. This driver can also be built as a module. If so, the module will be called lm85. -- cgit v1.2.3-70-g09d2 From f6c61cff8bcb58b8dfb645d4243a049908c02024 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:50 +0200 Subject: hwmon: (lm85) Add support for ADT7468 high-frequency PWM mode The ADT7468 supports a high-frequency PWM output mode where all PWM outputs are driven by a 22.5 kHz clock. Add support for this mode, and document it, as it may surprise the user that setting one PWM output frequency also affects the other PWM outputs. Signed-off-by: Jean Delvare Cc: Darrick J. Wong Acked-by: Guenter Roeck --- Documentation/hwmon/lm85 | 7 +++++++ drivers/hwmon/lm85.c | 35 ++++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm85 b/Documentation/hwmon/lm85 index 83cd5bb1ac79..239258a63c81 100644 --- a/Documentation/hwmon/lm85 +++ b/Documentation/hwmon/lm85 @@ -101,6 +101,13 @@ Devices has confirmed this "bug". The ADT7463 is reported to work as described in the documentation. The current lm85 driver does not show the offset register. +The ADT7468 has a high-frequency PWM mode, where all PWM outputs are +driven by a 22.5 kHz clock. This is a global mode, not per-PWM output, +which means that setting any PWM frequency above 11.3 kHz will switch +all 3 PWM outputs to a 22.5 kHz frequency. Conversely, setting any PWM +frequency below 11.3 kHz will switch all 3 PWM outputs to a frequency +between 10 and 100 Hz, which can then be tuned separately. + See the vendor datasheets for more information. There is application note from National (AN-1260) with some additional information about the LM85. The Analog Devices datasheet is very detailed and describes a procedure for diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 2e8f0c9458d4..1e229847f37a 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -64,9 +64,12 @@ enum chips { #define LM85_REG_VERSTEP 0x3f #define ADT7468_REG_CFG5 0x7c -#define ADT7468_OFF64 0x01 +#define ADT7468_OFF64 (1 << 0) +#define ADT7468_HFPWM (1 << 1) #define IS_ADT7468_OFF64(data) \ ((data)->type == adt7468 && !((data)->cfg5 & ADT7468_OFF64)) +#define IS_ADT7468_HFPWM(data) \ + ((data)->type == adt7468 && !((data)->cfg5 & ADT7468_HFPWM)) /* These are the recognized values for the above regs */ #define LM85_COMPANY_NATIONAL 0x01 @@ -567,8 +570,14 @@ static ssize_t show_pwm_freq(struct device *dev, { int nr = to_sensor_dev_attr(attr)->index; struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf, "%d\n", FREQ_FROM_REG(data->freq_map, - data->pwm_freq[nr])); + int freq; + + if (IS_ADT7468_HFPWM(data)) + freq = 22500; + else + freq = FREQ_FROM_REG(data->freq_map, data->pwm_freq[nr]); + + return sprintf(buf, "%d\n", freq); } static ssize_t set_pwm_freq(struct device *dev, @@ -580,10 +589,22 @@ static ssize_t set_pwm_freq(struct device *dev, long val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); - data->pwm_freq[nr] = FREQ_TO_REG(data->freq_map, val); - lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), - (data->zone[nr].range << 4) - | data->pwm_freq[nr]); + /* The ADT7468 has a special high-frequency PWM output mode, + * where all PWM outputs are driven by a 22.5 kHz clock. + * This might confuse the user, but there's not much we can do. */ + if (data->type == adt7468 && val >= 11300) { /* High freq. mode */ + data->cfg5 &= ~ADT7468_HFPWM; + lm85_write_value(client, ADT7468_REG_CFG5, data->cfg5); + } else { /* Low freq. mode */ + data->pwm_freq[nr] = FREQ_TO_REG(data->freq_map, val); + lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), + (data->zone[nr].range << 4) + | data->pwm_freq[nr]); + if (data->type == adt7468) { + data->cfg5 |= ADT7468_HFPWM; + lm85_write_value(client, ADT7468_REG_CFG5, data->cfg5); + } + } mutex_unlock(&data->update_lock); return count; } -- cgit v1.2.3-70-g09d2 From 44c1bcd4bcde32b2a31a6775a277706ab489c0dc Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 28 Oct 2010 20:31:51 +0200 Subject: hwmon: (it87) Add support for the IT8721F/IT8758E Add support for the IT8721F/IT8758E. These new chips differ from the older IT87xxF chips in the following ways: * ADC LSB is 12 mV instead of 16 mV. * PWM values are 8-bit instead of 7-bit. There are other minor changes we don't have to care about in the driver. Another change is that we will handle internal voltage scaling in the driver instead of delegating the work to user-space. Signed-off-by: Jean Delvare --- Documentation/hwmon/it87 | 28 ++++++++---- drivers/hwmon/Kconfig | 4 +- drivers/hwmon/it87.c | 116 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 115 insertions(+), 33 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index 8d08bf0d38ed..38425f0f2645 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 @@ -22,6 +22,10 @@ Supported chips: Prefix: 'it8720' Addresses scanned: from Super I/O config space (8 I/O ports) Datasheet: Not publicly available + * IT8721F/IT8758E + Prefix: 'it8721' + Addresses scanned: from Super I/O config space (8 I/O ports) + Datasheet: Not publicly available * SiS950 [clone of IT8705F] Prefix: 'it87' Addresses scanned: from Super I/O config space (8 I/O ports) @@ -67,7 +71,7 @@ Description ----------- This driver implements support for the IT8705F, IT8712F, IT8716F, -IT8718F, IT8720F, IT8726F and SiS950 chips. +IT8718F, IT8720F, IT8721F, IT8726F, IT8758E and SiS950 chips. These chips are 'Super I/O chips', supporting floppy disks, infrared ports, joysticks and other miscellaneous stuff. For hardware monitoring, they @@ -86,14 +90,15 @@ the driver won't notice and report changes in the VID value. The two upper VID bits share their pins with voltage inputs (in5 and in6) so you can't have both on a given board. -The IT8716F, IT8718F, IT8720F and later IT8712F revisions have support for -2 additional fans. The additional fans are supported by the driver. +The IT8716F, IT8718F, IT8720F, IT8721F/IT8758E and later IT8712F revisions +have support for 2 additional fans. The additional fans are supported by the +driver. -The IT8716F, IT8718F and IT8720F, and late IT8712F and IT8705F also have -optional 16-bit tachometer counters for fans 1 to 3. This is better (no more -fan clock divider mess) but not compatible with the older chips and -revisions. The 16-bit tachometer mode is enabled by the driver when one -of the above chips is detected. +The IT8716F, IT8718F, IT8720F and IT8721F/IT8758E, and late IT8712F and +IT8705F also have optional 16-bit tachometer counters for fans 1 to 3. This +is better (no more fan clock divider mess) but not compatible with the older +chips and revisions. The 16-bit tachometer mode is enabled by the driver when +one of the above chips is detected. The IT8726F is just bit enhanced IT8716F with additional hardware for AMD power sequencing. Therefore the chip will appear as IT8716F @@ -115,7 +120,12 @@ alarm is triggered if the voltage has crossed a programmable minimum or maximum limit. Note that minimum in this case always means 'closest to zero'; this is important for negative voltage measurements. All voltage inputs can measure voltages between 0 and 4.08 volts, with a resolution of -0.016 volt. The battery voltage in8 does not have limit registers. +0.016 volt (except IT8721F/IT8758E: 0.012 volt.) The battery voltage in8 does +not have limit registers. + +On the IT8721F/IT8758E, some voltage inputs are internal and scaled inside +the chip (in7, in8 and optionally in3). The driver handles this transparently +so user-space doesn't have to care. The VID lines (IT8712F/IT8716F/IT8718F/IT8720F) encode the core voltage value: the voltage level your processor should work with. This is hardcoded by diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index aefb9e6db237..a56f6adf3b76 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -428,8 +428,8 @@ config SENSORS_IT87 select HWMON_VID help If you say yes here you get support for ITE IT8705F, IT8712F, - IT8716F, IT8718F, IT8720F and IT8726F sensor chips, and the - SiS960 clone. + IT8716F, IT8718F, IT8720F, IT8721F, IT8726F and IT8758E sensor + chips, and the SiS960 clone. This driver can also be built as a module. If so, the module will be called it87. diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 7a3616ccbf05..14a5d981be7d 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -15,7 +15,9 @@ * IT8716F Super I/O chip w/LPC interface * IT8718F Super I/O chip w/LPC interface * IT8720F Super I/O chip w/LPC interface + * IT8721F Super I/O chip w/LPC interface * IT8726F Super I/O chip w/LPC interface + * IT8758E Super I/O chip w/LPC interface * Sis950 A clone of the IT8705F * * Copyright (C) 2001 Chris Gauthron @@ -54,7 +56,7 @@ #define DRVNAME "it87" -enum chips { it87, it8712, it8716, it8718, it8720 }; +enum chips { it87, it8712, it8716, it8718, it8720, it8721 }; static unsigned short force_id; module_param(force_id, ushort, 0); @@ -126,6 +128,7 @@ superio_exit(void) #define IT8716F_DEVID 0x8716 #define IT8718F_DEVID 0x8718 #define IT8720F_DEVID 0x8720 +#define IT8721F_DEVID 0x8721 #define IT8726F_DEVID 0x8726 #define IT87_ACT_REG 0x30 #define IT87_BASE_REG 0x60 @@ -229,6 +232,7 @@ struct it87_data { char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ + u16 in_scaled; /* Internal voltage sensors are scaled */ u8 in[9]; /* Register value */ u8 in_max[8]; /* Register value */ u8 in_min[8]; /* Register value */ @@ -260,8 +264,32 @@ struct it87_data { s8 auto_temp[3][5]; /* [nr][0] is point1_temp_hyst */ }; -#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8) / 16), 0, 255)) -#define IN_FROM_REG(val) ((val) * 16) +static u8 in_to_reg(const struct it87_data *data, int nr, long val) +{ + long lsb; + + if (data->type == it8721) { + if (data->in_scaled & (1 << nr)) + lsb = 24; + else + lsb = 12; + } else + lsb = 16; + + val = DIV_ROUND_CLOSEST(val, lsb); + return SENSORS_LIMIT(val, 0, 255); +} + +static int in_from_reg(const struct it87_data *data, int nr, int val) +{ + if (data->type == it8721) { + if (data->in_scaled & (1 << nr)) + return val * 24; + else + return val * 12; + } else + return val * 16; +} static inline u8 FAN_TO_REG(long rpm, int div) { @@ -289,8 +317,22 @@ static inline u16 FAN16_TO_REG(long rpm) ((val) + 500) / 1000), -128, 127)) #define TEMP_FROM_REG(val) ((val) * 1000) -#define PWM_TO_REG(val) ((val) >> 1) -#define PWM_FROM_REG(val) (((val) & 0x7f) << 1) +static u8 pwm_to_reg(const struct it87_data *data, long val) +{ + if (data->type == it8721) + return val; + else + return val >> 1; +} + +static int pwm_from_reg(const struct it87_data *data, u8 reg) +{ + if (data->type == it8721) + return reg; + else + return (reg & 0x7f) << 1; +} + static int DIV_TO_REG(int val) { @@ -321,7 +363,8 @@ static inline int has_16bit_fans(const struct it87_data *data) || (data->type == it8712 && data->revision >= 0x08) || data->type == it8716 || data->type == it8718 - || data->type == it8720; + || data->type == it8720 + || data->type == it8721; } static inline int has_old_autopwm(const struct it87_data *data) @@ -359,7 +402,7 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])); + return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in[nr])); } static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, @@ -369,7 +412,7 @@ static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])); + return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in_min[nr])); } static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, @@ -379,7 +422,7 @@ static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])); + return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in_max[nr])); } static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, @@ -395,7 +438,7 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&data->update_lock); - data->in_min[nr] = IN_TO_REG(val); + data->in_min[nr] = in_to_reg(data, nr, val); it87_write_value(data, IT87_REG_VIN_MIN(nr), data->in_min[nr]); mutex_unlock(&data->update_lock); @@ -414,7 +457,7 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&data->update_lock); - data->in_max[nr] = IN_TO_REG(val); + data->in_max[nr] = in_to_reg(data, nr, val); it87_write_value(data, IT87_REG_VIN_MAX(nr), data->in_max[nr]); mutex_unlock(&data->update_lock); @@ -644,7 +687,8 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm_duty[nr])); + return sprintf(buf, "%d\n", + pwm_from_reg(data, data->pwm_duty[nr])); } static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr, char *buf) @@ -814,7 +858,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&data->update_lock); - data->pwm_duty[nr] = PWM_TO_REG(val); + data->pwm_duty[nr] = pwm_to_reg(data, val); /* If we are in manual mode, write the duty cycle immediately; * otherwise, just store it for later use. */ if (!(data->pwm_ctrl[nr] & 0x80)) { @@ -918,7 +962,8 @@ static ssize_t show_auto_pwm(struct device *dev, int nr = sensor_attr->nr; int point = sensor_attr->index; - return sprintf(buf, "%d\n", PWM_FROM_REG(data->auto_pwm[nr][point])); + return sprintf(buf, "%d\n", + pwm_from_reg(data, data->auto_pwm[nr][point])); } static ssize_t set_auto_pwm(struct device *dev, @@ -935,7 +980,7 @@ static ssize_t set_auto_pwm(struct device *dev, return -EINVAL; mutex_lock(&data->update_lock); - data->auto_pwm[nr][point] = PWM_TO_REG(val); + data->auto_pwm[nr][point] = pwm_to_reg(data, val); it87_write_value(data, IT87_REG_AUTO_PWM(nr, point), data->auto_pwm[nr][point]); mutex_unlock(&data->update_lock); @@ -1205,9 +1250,16 @@ static ssize_t show_label(struct device *dev, struct device_attribute *attr, "5VSB", "Vbat", }; + static const char *labels_it8721[] = { + "+3.3V", + "3VSB", + "Vbat", + }; + struct it87_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr(attr)->index; - return sprintf(buf, "%s\n", labels[nr]); + return sprintf(buf, "%s\n", data->type == it8721 ? labels_it8721[nr] + : labels[nr]); } static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0); static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1); @@ -1492,6 +1544,9 @@ static int __init it87_find(unsigned short *address, case IT8720F_DEVID: sio_data->type = it8720; break; + case IT8721F_DEVID: + sio_data->type = it8721; + break; case 0xffff: /* No device at all */ goto exit; default: @@ -1532,11 +1587,17 @@ static int __init it87_find(unsigned short *address, int reg; superio_select(GPIO); - /* We need at least 4 VID pins */ + reg = superio_inb(IT87_SIO_GPIO3_REG); - if (reg & 0x0f) { - pr_info("it87: VID is disabled (pins used for GPIO)\n"); + if (sio_data->type == it8721) { + /* The IT8721F/IT8758E doesn't have VID pins at all */ sio_data->skip_vid = 1; + } else { + /* We need at least 4 VID pins */ + if (reg & 0x0f) { + pr_info("it87: VID is disabled (pins used for GPIO)\n"); + sio_data->skip_vid = 1; + } } /* Check if fan3 is there or not */ @@ -1574,7 +1635,7 @@ static int __init it87_find(unsigned short *address, } if (reg & (1 << 0)) sio_data->internal |= (1 << 0); - if (reg & (1 << 1)) + if ((reg & (1 << 1)) || sio_data->type == it8721) sio_data->internal |= (1 << 1); sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; @@ -1652,6 +1713,7 @@ static int __devinit it87_probe(struct platform_device *pdev) "it8716", "it8718", "it8720", + "it8721", }; res = platform_get_resource(pdev, IORESOURCE_IO, 0); @@ -1688,6 +1750,16 @@ static int __devinit it87_probe(struct platform_device *pdev) /* Check PWM configuration */ enable_pwm_interface = it87_check_pwm(dev); + /* Starting with IT8721F, we handle scaling of internal voltages */ + if (data->type == it8721) { + if (sio_data->internal & (1 << 0)) + data->in_scaled |= (1 << 3); /* in3 is AVCC */ + if (sio_data->internal & (1 << 1)) + data->in_scaled |= (1 << 7); /* in7 is VSB */ + if (sio_data->internal & (1 << 2)) + data->in_scaled |= (1 << 8); /* in8 is Vbat */ + } + /* Initialize the IT87 chip */ it87_init_device(pdev); @@ -2053,7 +2125,7 @@ static struct it87_data *it87_update_device(struct device *dev) data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE); /* The 8705 does not have VID capability. - The 8718 and the 8720 don't use IT87_REG_VID for the + The 8718 and later don't use IT87_REG_VID for the same purpose. */ if (data->type == it8712 || data->type == it8716) { data->vid = it87_read_value(data, IT87_REG_VID); @@ -2153,7 +2225,7 @@ static void __exit sm_it87_exit(void) MODULE_AUTHOR("Chris Gauthron, " "Jean Delvare "); -MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F/8720F/8726F, SiS950 driver"); +MODULE_DESCRIPTION("IT8705F/IT871xF/IT872xF hardware monitoring driver"); module_param(update_vbat, bool, 0); MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value"); module_param(fix_pwm_polarity, bool, 0); -- cgit v1.2.3-70-g09d2 From 4aad8f51d0672f1c95e2cf0e1bc7b9ab42d8e1ea Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Mon, 25 Oct 2010 10:14:05 -0500 Subject: kdb: Add kdb kernel module sample Add an example of how to add a dynamic kdb shell command via a kernel module. Signed-off-by: Jason Wessel --- Documentation/DocBook/kgdb.tmpl | 13 ++++++++- samples/Kconfig | 7 +++++ samples/Makefile | 2 +- samples/kdb/Makefile | 1 + samples/kdb/kdb_hello.c | 60 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 samples/kdb/Makefile create mode 100644 samples/kdb/kdb_hello.c (limited to 'Documentation') diff --git a/Documentation/DocBook/kgdb.tmpl b/Documentation/DocBook/kgdb.tmpl index 490d862c5f0d..d71b57fcf116 100644 --- a/Documentation/DocBook/kgdb.tmpl +++ b/Documentation/DocBook/kgdb.tmpl @@ -710,7 +710,18 @@ Task Addr Pid Parent [*] cpu State Thread Command A simple shell The kdb core command set A registration API to register additional kdb shell commands. - A good example of a self-contained kdb module is the "ftdump" command for dumping the ftrace buffer. See: kernel/trace/trace_kdb.c + + A good example of a self-contained kdb module + is the "ftdump" command for dumping the ftrace buffer. See: + kernel/trace/trace_kdb.c + For an example of how to dynamically register + a new kdb command you can build the kdb_hello.ko kernel module + from samples/kdb/kdb_hello.c. To build this example you can + set CONFIG_SAMPLES=y and CONFIG_SAMPLE_KDB=m in your kernel + config. Later run "modprobe kdb_hello" and the next time you + enter the kdb shell, you can run the "hello" + command. + The implementation for kdb_printf() which emits messages directly to I/O drivers, bypassing the kernel log. diff --git a/samples/Kconfig b/samples/Kconfig index 954a1d550c5f..e03cf0e374d7 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -54,4 +54,11 @@ config SAMPLE_KFIFO If in doubt, say "N" here. +config SAMPLE_KDB + tristate "Build kdb command exmaple -- loadable modules only" + depends on KGDB_KDB && m + help + Build an example of how to dynamically add the hello + command to the kdb shell. + endif # SAMPLES diff --git a/samples/Makefile b/samples/Makefile index 76b3c3455c29..f26c0959fd86 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -1,4 +1,4 @@ # Makefile for Linux samples code obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ tracepoints/ trace_events/ \ - hw_breakpoint/ kfifo/ + hw_breakpoint/ kfifo/ kdb/ diff --git a/samples/kdb/Makefile b/samples/kdb/Makefile new file mode 100644 index 000000000000..fbedf39d9356 --- /dev/null +++ b/samples/kdb/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SAMPLE_KDB) += kdb_hello.o diff --git a/samples/kdb/kdb_hello.c b/samples/kdb/kdb_hello.c new file mode 100644 index 000000000000..c1c2fa0f62c2 --- /dev/null +++ b/samples/kdb/kdb_hello.c @@ -0,0 +1,60 @@ +/* + * Created by: Jason Wessel + * + * Copyright (c) 2010 Wind River Systems, Inc. All Rights Reserved. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include + +/* + * All kdb shell command call backs receive argc and argv, where + * argv[0] is the command the end user typed + */ +static int kdb_hello_cmd(int argc, const char **argv) +{ + if (argc > 1) + return KDB_ARGCOUNT; + + if (argc) + kdb_printf("Hello %s.\n", argv[1]); + else + kdb_printf("Hello world!\n"); + + return 0; +} + + +static int __init kdb_hello_cmd_init(void) +{ + /* + * Registration of a dynamically added kdb command is done with + * kdb_register() with the arguments being: + * 1: The name of the shell command + * 2: The function that processes the command + * 3: Description of the usage of any arguments + * 4: Descriptive text when you run help + * 5: Number of characters to complete the command + * 0 == type the whole command + * 1 == match both "g" and "go" for example + */ + kdb_register("hello", kdb_hello_cmd, "[string]", + "Say Hello World or Hello [string]", 0); + return 0; +} + +static void __exit kdb_hello_cmd_exit(void) +{ + kdb_unregister("hello"); +} + +module_init(kdb_hello_cmd_init); +module_exit(kdb_hello_cmd_exit); + +MODULE_AUTHOR("WindRiver"); +MODULE_DESCRIPTION("KDB example to add a hello command"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From bb8430a2c8fe2b726033017daadf73c69b0348ea Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 31 Oct 2010 08:35:31 -0400 Subject: locks: remove fl_copy_lock lock_manager operation This one was only used for a nasty hack in nfsd, which has recently been removed. Signed-off-by: Christoph Hellwig Signed-off-by: Linus Torvalds --- Documentation/filesystems/Locking | 2 -- fs/locks.c | 5 +---- include/linux/fs.h | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 8a817f656f0a..a91f30890011 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -322,7 +322,6 @@ fl_release_private: yes yes prototypes: int (*fl_compare_owner)(struct file_lock *, struct file_lock *); void (*fl_notify)(struct file_lock *); /* unblock callback */ - void (*fl_copy_lock)(struct file_lock *, struct file_lock *); void (*fl_release_private)(struct file_lock *); void (*fl_break)(struct file_lock *); /* break_lease callback */ @@ -330,7 +329,6 @@ locking rules: BKL may block fl_compare_owner: yes no fl_notify: yes no -fl_copy_lock: yes no fl_release_private: yes yes fl_break: yes no diff --git a/fs/locks.c b/fs/locks.c index a2ab790471b5..65765cb6afed 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -235,11 +235,8 @@ static void locks_copy_private(struct file_lock *new, struct file_lock *fl) fl->fl_ops->fl_copy_lock(new, fl); new->fl_ops = fl->fl_ops; } - if (fl->fl_lmops) { - if (fl->fl_lmops->fl_copy_lock) - fl->fl_lmops->fl_copy_lock(new, fl); + if (fl->fl_lmops) new->fl_lmops = fl->fl_lmops; - } } /* diff --git a/include/linux/fs.h b/include/linux/fs.h index 1eb29399a4ff..334d68a17108 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1056,7 +1056,6 @@ struct lock_manager_operations { int (*fl_compare_owner)(struct file_lock *, struct file_lock *); void (*fl_notify)(struct file_lock *); /* unblock callback */ int (*fl_grant)(struct file_lock *, struct file_lock *, int); - void (*fl_copy_lock)(struct file_lock *, struct file_lock *); void (*fl_release_private)(struct file_lock *); void (*fl_break)(struct file_lock *); int (*fl_mylease)(struct file_lock *, struct file_lock *); -- cgit v1.2.3-70-g09d2 From e30d9859cf08920ae711f57ecd9726804451d29f Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Sun, 31 Oct 2010 21:06:59 +0100 Subject: i2c-i801: Add Intel Patsburg device ID Add support for the Intel Patsburg PCH SMBus Controller. Signed-off-by: Seth Heasley Signed-off-by: Jean Delvare --- Documentation/i2c/busses/i2c-i801 | 3 ++- drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-i801.c | 2 ++ include/linux/pci_ids.h | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801 index e307914a3eda..a417cb13943d 100644 --- a/Documentation/i2c/busses/i2c-i801 +++ b/Documentation/i2c/busses/i2c-i801 @@ -15,8 +15,9 @@ Supported adapters: * Intel 82801I (ICH9) * Intel EP80579 (Tolapai) * Intel 82801JI (ICH10) - * Intel 3400/5 Series (PCH) + * Intel 5/3400 Series (PCH) * Intel Cougar Point (PCH) + * Intel Patsburg (PCH) Datasheets: Publicly available at the Intel website Authors: diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index c950be3cce21..3a6321cb8030 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -99,6 +99,7 @@ config I2C_I801 ICH10 5/3400 Series (PCH) Cougar Point (PCH) + Patsburg (PCH) This driver can also be built as a module. If so, the module will be called i2c-i801. diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 59d65981eed7..1fe30da653c3 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -43,6 +43,7 @@ ICH10 0x3a60 32 hard yes yes yes 5/3400 Series (PCH) 0x3b30 32 hard yes yes yes Cougar Point (PCH) 0x1c22 32 hard yes yes yes + Patsburg (PCH) 0x1d22 32 hard yes yes yes Features supported by this driver: Software PEC no @@ -592,6 +593,7 @@ static const struct pci_device_id i801_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_5) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS) }, { 0, } }; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 87e2c2e7aed3..c6bcfe93b9ca 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2465,6 +2465,7 @@ #define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22 #define PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN 0x1c41 #define PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX 0x1c5f +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22 #define PCI_DEVICE_ID_INTEL_PATSBURG_LPC 0x1d40 #define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410 #define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411 -- cgit v1.2.3-70-g09d2 From 55fee8d7f7cb011d7f161cf816220ac74e34b6b1 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 31 Oct 2010 21:07:00 +0100 Subject: i2c-i801: Add PCI idents for Patsburg 'IDF' SMBus controllers These are the extra 'Integrated Device Function' SMBus controllers found on the Patsburg chipset. Mention the absence of slave mode support. Signed-off-by: David Woodhouse Signed-off-by: Jean Delvare --- Documentation/i2c/busses/i2c-i801 | 3 +++ drivers/i2c/busses/i2c-i801.c | 12 ++++++++++++ 2 files changed, 15 insertions(+) (limited to 'Documentation') diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801 index a417cb13943d..93fe76e56522 100644 --- a/Documentation/i2c/busses/i2c-i801 +++ b/Documentation/i2c/busses/i2c-i801 @@ -20,6 +20,9 @@ Supported adapters: * Intel Patsburg (PCH) Datasheets: Publicly available at the Intel website +On Intel Patsburg and later chipsets, both the normal host SMBus controller +and the additional 'Integrated Device Function' controllers are supported. + Authors: Mark Studebaker Jean Delvare diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 56883f48c2e4..02835ce7ff4b 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -46,6 +46,9 @@ 5/3400 Series (PCH) 0x3b30 32 hard yes yes yes Cougar Point (PCH) 0x1c22 32 hard yes yes yes Patsburg (PCH) 0x1d22 32 hard yes yes yes + Patsburg (PCH) IDF 0x1d70 32 hard yes yes yes + Patsburg (PCH) IDF 0x1d71 32 hard yes yes yes + Patsburg (PCH) IDF 0x1d72 32 hard yes yes yes Features supported by this driver: Software PEC no @@ -53,6 +56,7 @@ Block buffer yes Block process call transaction no I2C block read transaction yes (doesn't use the block buffer) + Slave mode no See the file Documentation/i2c/busses/i2c-i801 for details. */ @@ -128,6 +132,11 @@ SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR | \ SMBHSTSTS_INTR) +/* Patsburg also has three 'Integrated Device Function' SMBus controllers */ +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0 0x1d70 +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1 0x1d71 +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2 0x1d72 + struct i801_priv { struct i2c_adapter adapter; unsigned long smba; @@ -608,6 +617,9 @@ static const struct pci_device_id i801_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2) }, { 0, } }; -- cgit v1.2.3-70-g09d2