From 66c23551b1b774e2be3c7bdf91c0ebf2c7a3519e Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 13 May 2008 13:26:03 +0300 Subject: ARM: OMAP: DMA: Don't mark channel active in omap_enable_channel_irq Channel should be marked active only when DMA is really started. Otherwise just omap_request_dma, omap_dma_link_lch and omap_dma_unlink_lch will cause incorrect dump_stack(). Signed-off-by: Jarkko Nikula Signed-off-by: Tony Lindgren diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c index c00eda5..39c637b 100644 --- a/arch/arm/plat-omap/dma.c +++ b/arch/arm/plat-omap/dma.c @@ -501,8 +501,6 @@ static inline void omap_enable_channel_irq(int lch) /* Enable some nice interrupts. */ OMAP_DMA_CICR_REG(lch) = dma_chan[lch].enabled_irqs; - - dma_chan[lch].flags |= OMAP_DMA_ACTIVE; } static void omap_disable_channel_irq(int lch) -- cgit v0.10.2 From 15e02a3b510aa4ef3d077ebc25eb3cd08b9af034 Mon Sep 17 00:00:00 2001 From: Thara Gopinath Date: Mon, 28 Apr 2008 16:55:01 +0530 Subject: ARM: OMAP: Correcting the gpmc prefetch control register address Correcting the GPMC_PREFETCH_CONTROL register address Signed-off-by: Thara Gopinath Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 02cede2..dbf68dc 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -42,7 +42,7 @@ #define GPMC_STATUS 0x54 #define GPMC_PREFETCH_CONFIG1 0x1e0 #define GPMC_PREFETCH_CONFIG2 0x1e4 -#define GPMC_PREFETCH_CONTROL 0x1e8 +#define GPMC_PREFETCH_CONTROL 0x1ec #define GPMC_PREFETCH_STATUS 0x1f0 #define GPMC_ECC_CONFIG 0x1f4 #define GPMC_ECC_CONTROL 0x1f8 -- cgit v0.10.2 From 50db04dd9c74178e68a981a7127c37252ffb3242 Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Sun, 15 Jun 2008 00:47:36 +0200 Subject: debugobjects: fix lockdep warning Daniel J Blueman reported: | ======================================================= | [ INFO: possible circular locking dependency detected ] | 2.6.26-rc5-201c #1 | ------------------------------------------------------- | nscd/3669 is trying to acquire lock: | (&n->list_lock){.+..}, at: [] deactivate_slab+0x173/0x1e0 | | but task is already holding lock: | (&obj_hash[i].lock){++..}, at: [] | __debug_object_init+0x2f/0x350 | | which lock already depends on the new lock. There are two locks involved here; the first is a SLUB-local lock, and the second is a debugobjects-local lock. They are basically taken in two different orders: 1. SLUB { debugobjects { ... } } 2. debugobjects { SLUB { ... } } This patch changes pattern #2 by trying to fill the memory pool (e.g. the call into SLUB/kmalloc()) outside the debugobjects lock, so now the two patterns look like this: 1. SLUB { debugobjects { ... } } 2. SLUB { } debugobjects { ... } [ daniel.blueman@gmail.com: pool_lock needs to be taken irq safe in fill_pool ] Reported-by: Daniel J Blueman Signed-off-by: Vegard Nossum Signed-off-by: Thomas Gleixner diff --git a/lib/debugobjects.c b/lib/debugobjects.c index a76a5e1..85b18d7 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -68,6 +68,7 @@ static int fill_pool(void) { gfp_t gfp = GFP_ATOMIC | __GFP_NORETRY | __GFP_NOWARN; struct debug_obj *new; + unsigned long flags; if (likely(obj_pool_free >= ODEBUG_POOL_MIN_LEVEL)) return obj_pool_free; @@ -81,10 +82,10 @@ static int fill_pool(void) if (!new) return obj_pool_free; - spin_lock(&pool_lock); + spin_lock_irqsave(&pool_lock, flags); hlist_add_head(&new->node, &obj_pool); obj_pool_free++; - spin_unlock(&pool_lock); + spin_unlock_irqrestore(&pool_lock, flags); } return obj_pool_free; } @@ -110,16 +111,13 @@ static struct debug_obj *lookup_object(void *addr, struct debug_bucket *b) } /* - * Allocate a new object. If the pool is empty and no refill possible, - * switch off the debugger. + * Allocate a new object. If the pool is empty, switch off the debugger. */ static struct debug_obj * alloc_object(void *addr, struct debug_bucket *b, struct debug_obj_descr *descr) { struct debug_obj *obj = NULL; - int retry = 0; -repeat: spin_lock(&pool_lock); if (obj_pool.first) { obj = hlist_entry(obj_pool.first, typeof(*obj), node); @@ -141,9 +139,6 @@ repeat: } spin_unlock(&pool_lock); - if (fill_pool() && !obj && !retry++) - goto repeat; - return obj; } @@ -261,6 +256,8 @@ __debug_object_init(void *addr, struct debug_obj_descr *descr, int onstack) struct debug_obj *obj; unsigned long flags; + fill_pool(); + db = get_bucket((unsigned long) addr); spin_lock_irqsave(&db->lock, flags); -- cgit v0.10.2 From f1edfc420ac7beb90b27bf822036cbbfa32483f1 Mon Sep 17 00:00:00 2001 From: Jaya Kumar Date: Sun, 22 Jun 2008 04:27:25 +0100 Subject: [ARM] 5115/1: pxafb: fix ifdef for command line option handling This bug was found and fixed by Lothar Wassmann. Previously, the use of ifndef CONFIG_MODULES made it such that pxafb command line option parsing was dependent on whether the kernel was built with module support. The ifndef should be MODULE so that parsing is dependent only on whether the driver is built-in or not. Signed-off-by: Jaya Kumar Acked-by: Krzysztof Helt Acked-by: Eric Miao Signed-off-by: Russell King diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 7dcda18..eb23d19 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -1588,7 +1588,7 @@ static int __init pxafb_parse_options(struct device *dev, char *options) static char g_options[256] __devinitdata = ""; -#ifndef CONFIG_MODULES +#ifndef MODULE static int __devinit pxafb_setup_options(void) { char *options = NULL; -- cgit v0.10.2 From ee98476bbc565f8fe42e198602e647288b6a258d Mon Sep 17 00:00:00 2001 From: Jaya Kumar Date: Sun, 22 Jun 2008 04:27:26 +0100 Subject: [ARM] 5116/1: pxafb: cleanup and fix order of failure handling This issue was found by Krzysztof Helt and Eric Miao. pxafb had issues in the order with which it cleaned up if errors occurred during a probe. This patch reorders the failure handling sequence and also frees the cmap and clk. Signed-off-by: Jaya Kumar Acked-by: Krzysztof Helt Acked-by: Eric Miao Signed-off-by: Russell King diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index eb23d19..796e0d1 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -1685,14 +1685,14 @@ static int __init pxafb_probe(struct platform_device *dev) if (r == NULL) { dev_err(&dev->dev, "no I/O memory resource defined\n"); ret = -ENODEV; - goto failed; + goto failed_fbi; } r = request_mem_region(r->start, r->end - r->start + 1, dev->name); if (r == NULL) { dev_err(&dev->dev, "failed to request I/O memory\n"); ret = -EBUSY; - goto failed; + goto failed_fbi; } fbi->mmio_base = ioremap(r->start, r->end - r->start + 1); @@ -1735,8 +1735,17 @@ static int __init pxafb_probe(struct platform_device *dev) * This makes sure that our colour bitfield * descriptors are correctly initialised. */ - pxafb_check_var(&fbi->fb.var, &fbi->fb); - pxafb_set_par(&fbi->fb); + ret = pxafb_check_var(&fbi->fb.var, &fbi->fb); + if (ret) { + dev_err(&dev->dev, "failed to get suitable mode\n"); + goto failed_free_irq; + } + + ret = pxafb_set_par(&fbi->fb); + if (ret) { + dev_err(&dev->dev, "Failed to set parameters\n"); + goto failed_free_irq; + } platform_set_drvdata(dev, fbi); @@ -1744,7 +1753,7 @@ static int __init pxafb_probe(struct platform_device *dev) if (ret < 0) { dev_err(&dev->dev, "Failed to register framebuffer device: %d\n", ret); - goto failed_free_irq; + goto failed_free_cmap; } #ifdef CONFIG_CPU_FREQ @@ -1763,18 +1772,23 @@ static int __init pxafb_probe(struct platform_device *dev) return 0; +failed_free_cmap: + if (fbi->fb.cmap.len) + fb_dealloc_cmap(&fbi->fb.cmap); failed_free_irq: free_irq(irq, fbi); -failed_free_res: - release_mem_region(r->start, r->end - r->start + 1); -failed_free_io: - iounmap(fbi->mmio_base); failed_free_mem: dma_free_writecombine(&dev->dev, fbi->map_size, fbi->map_cpu, fbi->map_dma); -failed: +failed_free_io: + iounmap(fbi->mmio_base); +failed_free_res: + release_mem_region(r->start, r->end - r->start + 1); +failed_fbi: + clk_put(fbi->clk); platform_set_drvdata(dev, NULL); kfree(fbi); +failed: return ret; } -- cgit v0.10.2 From e5a2c9ccb3e849fed70674ac6880536eaf553dba Mon Sep 17 00:00:00 2001 From: Uli Luckas Date: Wed, 18 Jun 2008 09:54:03 +0100 Subject: [ARM] 5109/1: Mark rtc sa1100 driver as wakeup source before registering it Mark rtc sa1100 driver as wakeup source before registering it. rtc_device_register evaluates device_can_wakeup(rtc->dev.parent) and supresses the creation of /sys/class/rtc/rtcX/wakealarm if device_can_wakeup is not (yet) true. Signed-off-by: Uli Luckas Signed-off-by: Russell King diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 82f62d2..67421b0d 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -331,14 +331,14 @@ static int sa1100_rtc_probe(struct platform_device *pdev) RCNR = 0; } + device_init_wakeup(&pdev->dev, 1); + rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) return PTR_ERR(rtc); - device_init_wakeup(&pdev->dev, 1); - platform_set_drvdata(pdev, rtc); return 0; -- cgit v0.10.2 From fc6e14f4fb50bc734d08e65eb8a5798d54a2ad77 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 22 Jun 2008 15:41:30 +0100 Subject: [ARM] Export dma_sync_sg_for_device() Noticed by Martin Michlmayr, this missing export prevents IEEE1394 from building with: ERROR: "dma_sync_sg_for_device" [drivers/ieee1394/ieee1394.ko] undefined! Signed-off-by: Russell King diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c index 52fc6a8..2744673 100644 --- a/arch/arm/common/dmabounce.c +++ b/arch/arm/common/dmabounce.c @@ -650,7 +650,8 @@ EXPORT_SYMBOL(dma_map_sg); EXPORT_SYMBOL(dma_unmap_sg); EXPORT_SYMBOL(dma_sync_single_for_cpu); EXPORT_SYMBOL(dma_sync_single_for_device); -EXPORT_SYMBOL(dma_sync_sg); +EXPORT_SYMBOL(dma_sync_sg_for_cpu); +EXPORT_SYMBOL(dma_sync_sg_for_device); EXPORT_SYMBOL(dmabounce_register_dev); EXPORT_SYMBOL(dmabounce_unregister_dev); -- cgit v0.10.2 From fe6e9c1f25ac01f848bd084ee0ee62a5a0966ff3 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 23 Jun 2008 08:30:55 -0400 Subject: [PATCH] fix cgroup-inflicted breakage in block_dev.c devcgroup_inode_permission() expects MAY_FOO, not FMODE_FOO; kindly keep your misdesign consistent if you positively have to inflict it on the kernel. Signed-off-by: Al Viro diff --git a/fs/block_dev.c b/fs/block_dev.c index 470c10c..10d8a0a 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -931,8 +931,16 @@ static int do_open(struct block_device *bdev, struct file *file, int for_part) struct gendisk *disk; int ret; int part; + int perm = 0; - ret = devcgroup_inode_permission(bdev->bd_inode, file->f_mode); + if (file->f_mode & FMODE_READ) + perm |= MAY_READ; + if (file->f_mode & FMODE_WRITE) + perm |= MAY_WRITE; + /* + * hooks: /n/, see "layering violations". + */ + ret = devcgroup_inode_permission(bdev->bd_inode, perm); if (ret != 0) return ret; -- cgit v0.10.2 From 12fd0d3088d27867be68655bcab2b074f2835f60 Mon Sep 17 00:00:00 2001 From: Michael Kerrisk Date: Mon, 9 Jun 2008 21:16:07 -0700 Subject: [patch for 2.6.26 2/4] vfs: utimensat(): be consistent with utime() for immutable and append-only files This patch fixes utimensat() to make its behavior consistent with that of utime()/utimes() when dealing with files marked immutable and append-only. The current utimensat() implementation also returns EPERM if 'times' is non-NULL and the tv_nsec fields are both UTIME_NOW. For consistency, the (times != NULL && times[0].tv_nsec == UTIME_NOW && times[1].tv_nsec == UTIME_NOW) case should be treated like the traditional utimes() case where 'times' is NULL. That is, the call should succeed for a file marked append-only and should give the error EACCES if the file is marked as immutable. The simple way to do this is to set 'times' to NULL if (times[0].tv_nsec == UTIME_NOW && times[1].tv_nsec == UTIME_NOW). This is also the natural approach, since POSIX.1 semantics consider the times == {{x, UTIME_NOW}, {y, UTIME_NOW}} to be exactly equivalent to the case for times == NULL. (Thanks to Miklos for pointing this out.) Patch 3 in this series relies on the simplification provided by this patch. Acked-by: Miklos Szeredi Cc: Al Viro Cc: Ulrich Drepper Signed-off-by: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Al Viro diff --git a/fs/utimes.c b/fs/utimes.c index af059d5..14d3edb 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -102,6 +102,10 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags if (error) goto dput_and_out; + if (times && times[0].tv_nsec == UTIME_NOW && + times[1].tv_nsec == UTIME_NOW) + times = NULL; + /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { -- cgit v0.10.2 From 94c70b9ba7e9c1036284e779e2fef5be89021533 Mon Sep 17 00:00:00 2001 From: Michael Kerrisk Date: Mon, 9 Jun 2008 21:16:05 -0700 Subject: [patch for 2.6.26 1/4] vfs: utimensat(): ignore tv_sec if tv_nsec == UTIME_OMIT or UTIME_NOW The POSIX.1 draft spec for utimensat() says that if a times[n].tv_nsec field is UTIME_OMIT or UTIME_NOW, then the value in the corresponding tv_sec field is ignored. See the last sentence of this para, from the spec: If the tv_nsec field of a timespec structure has the special value UTIME_NOW, the file's relevant timestamp shall be set to the greatest value supported by the file system that is not greater than the current time. If the tv_nsec field has the special value UTIME_OMIT, the file's relevant timestamp shall not be changed. In either case, the tv_sec field shall be ignored. However the current Linux implementation requires the tv_sec value to be zero (or the EINVAL error results). This requirement should be removed. Acked-by: Miklos Szeredi Cc: Al Viro Cc: Ulrich Drepper Signed-off-by: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Al Viro diff --git a/fs/utimes.c b/fs/utimes.c index 14d3edb..d466bc5 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -173,14 +173,6 @@ asmlinkage long sys_utimensat(int dfd, char __user *filename, struct timespec __ if (utimes) { if (copy_from_user(&tstimes, utimes, sizeof(tstimes))) return -EFAULT; - if ((tstimes[0].tv_nsec == UTIME_OMIT || - tstimes[0].tv_nsec == UTIME_NOW) && - tstimes[0].tv_sec != 0) - return -EINVAL; - if ((tstimes[1].tv_nsec == UTIME_OMIT || - tstimes[1].tv_nsec == UTIME_NOW) && - tstimes[1].tv_sec != 0) - return -EINVAL; /* Nothing to do, we must not even check the path. */ if (tstimes[0].tv_nsec == UTIME_OMIT && -- cgit v0.10.2 From 4cca92264e61a90b43fc4e076cd25b7f4e16dc61 Mon Sep 17 00:00:00 2001 From: Michael Kerrisk Date: Mon, 9 Jun 2008 21:16:08 -0700 Subject: [patch for 2.6.26 3/4] vfs: utimensat(): fix error checking for {UTIME_NOW,UTIME_OMIT} case The POSIX.1 draft spec for utimensat() says: Only a process with the effective user ID equal to the user ID of the file or with appropriate privileges may use futimens() or utimensat() with a non-null times argument that does not have both tv_nsec fields set to UTIME_NOW and does not have both tv_nsec fields set to UTIME_OMIT. If this condition is violated, then the error EPERM should result. However, the current implementation does not generate EPERM if one tv_nsec field is UTIME_NOW while the other is UTIME_OMIT. It should give this error for that case. This patch: a) Repairs that problem. b) Removes the now unneeded nsec_special() helper function. c) Adds some comments to explain the checks that are being performed. Thanks to Miklos, who provided comments on the previous iteration of this patch. As a result, this version is a little simpler and and its logic is better structured. Miklos suggested an alternative idea, migrating the is_owner_or_cap() checks into fs/attr.c:inode_change_ok() via the use of an ATTR_OWNER_CHECK flag. Maybe we could do that later, but for now I've gone with this version, which is IMO simpler, and can be more easily read as being correct. Acked-by: Miklos Szeredi Cc: Al Viro Cc: Ulrich Drepper Signed-off-by: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Al Viro diff --git a/fs/utimes.c b/fs/utimes.c index d466bc5..118d1c3 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -40,14 +40,9 @@ asmlinkage long sys_utime(char __user *filename, struct utimbuf __user *times) #endif -static bool nsec_special(long nsec) -{ - return nsec == UTIME_OMIT || nsec == UTIME_NOW; -} - static bool nsec_valid(long nsec) { - if (nsec_special(nsec)) + if (nsec == UTIME_OMIT || nsec == UTIME_NOW) return true; return nsec >= 0 && nsec <= 999999999; @@ -106,7 +101,7 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags times[1].tv_nsec == UTIME_NOW) times = NULL; - /* Don't worry, the checks are done in inode_change_ok() */ + /* In most cases, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { error = -EPERM; @@ -128,15 +123,26 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; newattrs.ia_valid |= ATTR_MTIME_SET; } - } - /* - * If times is NULL or both times are either UTIME_OMIT or - * UTIME_NOW, then need to check permissions, because - * inode_change_ok() won't do it. - */ - if (!times || (nsec_special(times[0].tv_nsec) && - nsec_special(times[1].tv_nsec))) { + /* + * For the UTIME_OMIT/UTIME_NOW and UTIME_NOW/UTIME_OMIT + * cases, we need to make an extra check that is not done by + * inode_change_ok(). + */ + if (((times[0].tv_nsec == UTIME_NOW && + times[1].tv_nsec == UTIME_OMIT) + || + (times[0].tv_nsec == UTIME_OMIT && + times[1].tv_nsec == UTIME_NOW)) + && !is_owner_or_cap(inode)) + goto mnt_drop_write_and_out; + } else { + + /* + * If times is NULL (or both times are UTIME_NOW), + * then we need to check permissions, because + * inode_change_ok() won't do it. + */ error = -EACCES; if (IS_IMMUTABLE(inode)) goto mnt_drop_write_and_out; -- cgit v0.10.2 From c70f84417429f41519be0197a1092a53c2201f47 Mon Sep 17 00:00:00 2001 From: Michael Kerrisk Date: Mon, 9 Jun 2008 21:16:09 -0700 Subject: [patch for 2.6.26 4/4] vfs: utimensat(): fix write access check for futimens() The POSIX.1 draft spec for futimens()/utimensat() says: Only a process with the effective user ID equal to the user ID of the file, *or with write access to the file*, or with appropriate privileges may use futimens() or utimensat() with a null pointer as the times argument or with both tv_nsec fields set to the special value UTIME_NOW. The important piece here is "with write access to the file", and this matters for futimens(), which deals with an argument that is a file descriptor referring to the file whose timestamps are being updated, The standard is saying that the "writability" check is based on the file permissions, not the access mode with which the file is opened. (This behavior is consistent with the semantics of FreeBSD's futimes().) However, Linux is currently doing the latter -- futimens(fd, times) is a library function implemented as utimensat(fd, NULL, times, 0) and within the utimensat() implementation we have the code: f = fget(dfd); // dfd is 'fd' ... if (f) { if (!(f->f_mode & FMODE_WRITE)) goto mnt_drop_write_and_out; The check should instead be based on the file permissions. Thanks to Miklos for pointing out how to do this check. Miklos also pointed out a simplification that could be made to my first version of this patch, since the checks for the pathname and file descriptor cases can now be conflated. Acked-by: Miklos Szeredi Cc: Al Viro Cc: Ulrich Drepper Signed-off-by: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Al Viro diff --git a/fs/utimes.c b/fs/utimes.c index 118d1c3..b6b664e 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -148,14 +148,9 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags goto mnt_drop_write_and_out; if (!is_owner_or_cap(inode)) { - if (f) { - if (!(f->f_mode & FMODE_WRITE)) - goto mnt_drop_write_and_out; - } else { - error = vfs_permission(&nd, MAY_WRITE); - if (error) - goto mnt_drop_write_and_out; - } + error = permission(inode, MAY_WRITE, NULL); + if (error) + goto mnt_drop_write_and_out; } } mutex_lock(&inode->i_mutex); -- cgit v0.10.2 From c8e7f449b225ee6c87454ac069f0a041035c5140 Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Mon, 9 Jun 2008 16:40:35 -0700 Subject: [patch 1/4] vfs: path_{get,put}() cleanups Here are some more places where path_{get,put}() can be used instead of dput()/mntput() pair. Signed-off-by: Jan Blunck Cc: Al Viro Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Al Viro diff --git a/fs/namei.c b/fs/namei.c index c7e4353..ee15446 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -581,15 +581,13 @@ static __always_inline int link_path_walk(const char *name, struct nameidata *nd int result; /* make sure the stuff we saved doesn't go away */ - dget(save.dentry); - mntget(save.mnt); + path_get(&save); result = __link_path_walk(name, nd); if (result == -ESTALE) { /* nd->path had been dropped */ nd->path = save; - dget(nd->path.dentry); - mntget(nd->path.mnt); + path_get(&nd->path); nd->flags |= LOOKUP_REVAL; result = __link_path_walk(name, nd); } @@ -1216,8 +1214,9 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, nd->flags = flags; nd->depth = 0; - nd->path.mnt = mntget(mnt); - nd->path.dentry = dget(dentry); + nd->path.dentry = dentry; + nd->path.mnt = mnt; + path_get(&nd->path); retval = path_walk(name, nd); if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry && diff --git a/fs/pipe.c b/fs/pipe.c index ec228bc..700f4e0 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -1003,8 +1003,7 @@ struct file *create_write_pipe(void) void free_write_pipe(struct file *f) { free_pipe_info(f->f_dentry->d_inode); - dput(f->f_path.dentry); - mntput(f->f_path.mnt); + path_put(&f->f_path); put_filp(f); } @@ -1015,8 +1014,8 @@ struct file *create_read_pipe(struct file *wrf) return ERR_PTR(-ENFILE); /* Grab pipe from the writer */ - f->f_path.mnt = mntget(wrf->f_path.mnt); - f->f_path.dentry = dget(wrf->f_path.dentry); + f->f_path = wrf->f_path; + path_get(&wrf->f_path); f->f_mapping = wrf->f_path.dentry->d_inode->i_mapping; f->f_pos = 0; @@ -1068,8 +1067,7 @@ int do_pipe(int *fd) err_fdr: put_unused_fd(fdr); err_read_pipe: - dput(fr->f_dentry); - mntput(fr->f_vfsmnt); + path_put(&fr->f_path); put_filp(fr); err_write_pipe: free_write_pipe(fw); -- cgit v0.10.2 From 20d4fdc1a788e4ca0aaf2422772ba668e7e10839 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 9 Jun 2008 16:40:36 -0700 Subject: [patch 2/4] fs: make struct file arg to d_path const Signed-off-by: Jan Engelhardt Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Al Viro diff --git a/fs/dcache.c b/fs/dcache.c index 3ee588d5..c4c9072 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1847,7 +1847,7 @@ Elong: * * "buflen" should be positive. Caller holds the dcache_lock. */ -char *d_path(struct path *path, char *buf, int buflen) +char *d_path(const struct path *path, char *buf, int buflen) { char *res; struct path root; diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 2a66394..d982eb8 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -300,7 +300,7 @@ extern int d_validate(struct dentry *, struct dentry *); extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); extern char *__d_path(const struct path *path, struct path *root, char *, int); -extern char *d_path(struct path *, char *, int); +extern char *d_path(const struct path *, char *, int); extern char *dentry_path(struct dentry *, char *, int); /* Allocation counts.. */ -- cgit v0.10.2 From 694a1764d657e0f7a9b139bc7269c8d5f5a2534b Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Mon, 9 Jun 2008 16:40:37 -0700 Subject: [patch 3/4] vfs: fix ERR_PTR abuse in generic_readlink generic_readlink calls ERR_PTR for negative and positive values (vfs_readlink returns length of "link"), but it should not (not an errno) and does not need to. Signed-off-by: Marcin Slusarz Cc: Al Viro Cc: Christoph Hellwig Acked-by: Miklos Szeredi Signed-off-by: Andrew Morton Signed-off-by: Al Viro diff --git a/fs/namei.c b/fs/namei.c index ee15446..01e67dd 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2856,16 +2856,17 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) { struct nameidata nd; void *cookie; + int res; nd.depth = 0; cookie = dentry->d_inode->i_op->follow_link(dentry, &nd); - if (!IS_ERR(cookie)) { - int res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd)); - if (dentry->d_inode->i_op->put_link) - dentry->d_inode->i_op->put_link(dentry, &nd, cookie); - cookie = ERR_PTR(res); - } - return PTR_ERR(cookie); + if (IS_ERR(cookie)) + return PTR_ERR(cookie); + + res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd)); + if (dentry->d_inode->i_op->put_link) + dentry->d_inode->i_op->put_link(dentry, &nd, cookie); + return res; } int vfs_follow_link(struct nameidata *nd, const char *link) -- cgit v0.10.2 From f9f48ec72bfc9489a30bc6ddbfcf27d86a8bc651 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Mon, 9 Jun 2008 16:40:38 -0700 Subject: [patch 4/4] flock: remove unused fields from file_lock_operations fl_insert and fl_remove are not used right now in the kernel. Remove them. Signed-off-by: Denis V. Lunev Cc: Matthew Wilcox Cc: Alexander Viro Cc: "J. Bruce Fields" Signed-off-by: Andrew Morton Signed-off-by: Al Viro diff --git a/fs/locks.c b/fs/locks.c index 11dbf08..dce8c747 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -561,9 +561,6 @@ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) /* insert into file's list */ fl->fl_next = *pos; *pos = fl; - - if (fl->fl_ops && fl->fl_ops->fl_insert) - fl->fl_ops->fl_insert(fl); } /* @@ -586,9 +583,6 @@ static void locks_delete_lock(struct file_lock **thisfl_p) fl->fl_fasync = NULL; } - if (fl->fl_ops && fl->fl_ops->fl_remove) - fl->fl_ops->fl_remove(fl); - if (fl->fl_nspid) { put_pid(fl->fl_nspid); fl->fl_nspid = NULL; diff --git a/include/linux/fs.h b/include/linux/fs.h index d490779..7c10808 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -894,8 +894,6 @@ static inline int file_check_writeable(struct file *filp) typedef struct files_struct *fl_owner_t; struct file_lock_operations { - void (*fl_insert)(struct file_lock *); /* lock insertion callback */ - void (*fl_remove)(struct file_lock *); /* lock removal callback */ void (*fl_copy_lock)(struct file_lock *, struct file_lock *); void (*fl_release_private)(struct file_lock *); }; -- cgit v0.10.2 From be285c712bbbe5db43e503782fbef2bfeaa345f9 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Mon, 16 Jun 2008 13:28:07 +0200 Subject: [patch 3/3] vfs: make d_path() consistent across mount operations The path that __d_path() computes can become slightly inconsistent when it races with mount operations: it grabs the vfsmount_lock when traversing mount points but immediately drops it again, only to re-grab it when it reaches the next mount point. The result is that the filename computed is not always consisent, and the file may never have had that name. (This is unlikely, but still possible.) Fix this by grabbing the vfsmount_lock for the whole duration of __d_path(). Signed-off-by: Andreas Gruenbacher Signed-off-by: John Johansen Signed-off-by: Miklos Szeredi Acked-by: Christoph Hellwig Signed-off-by: Al Viro diff --git a/fs/dcache.c b/fs/dcache.c index c4c9072..2b479de 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1782,6 +1782,7 @@ char *__d_path(const struct path *path, struct path *root, char * end = buffer+buflen; char * retval; + spin_lock(&vfsmount_lock); prepend(&end, &buflen, "\0", 1); if (!IS_ROOT(dentry) && d_unhashed(dentry) && (prepend(&end, &buflen, " (deleted)", 10) != 0)) @@ -1800,14 +1801,11 @@ char *__d_path(const struct path *path, struct path *root, break; if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { /* Global root? */ - spin_lock(&vfsmount_lock); if (vfsmnt->mnt_parent == vfsmnt) { - spin_unlock(&vfsmount_lock); goto global_root; } dentry = vfsmnt->mnt_mountpoint; vfsmnt = vfsmnt->mnt_parent; - spin_unlock(&vfsmount_lock); continue; } parent = dentry->d_parent; @@ -1820,6 +1818,8 @@ char *__d_path(const struct path *path, struct path *root, dentry = parent; } +out: + spin_unlock(&vfsmount_lock); return retval; global_root: @@ -1829,9 +1829,11 @@ global_root: goto Elong; root->mnt = vfsmnt; root->dentry = dentry; - return retval; + goto out; + Elong: - return ERR_PTR(-ENAMETOOLONG); + retval = ERR_PTR(-ENAMETOOLONG); + goto out; } /** -- cgit v0.10.2 From 31f3e0b3a18c6d48196c40a82a3b8c01f4ff6b23 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 23 Jun 2008 18:11:52 +0200 Subject: [patch 1/3] vfs: dcache sparse fixes Fix the following sparse warnings: fs/dcache.c:2183:19: warning: symbol 'filp_cachep' was not declared. Should it be static? fs/dcache.c:115:3: warning: context imbalance in 'dentry_iput' - unexpected unlock fs/dcache.c:188:2: warning: context imbalance in 'dput' - different lock contexts for basic block fs/dcache.c:400:2: warning: context imbalance in 'prune_one_dentry' - different lock contexts for basic block fs/dcache.c:431:22: warning: context imbalance in 'prune_dcache' - different lock contexts for basic block fs/dcache.c:563:2: warning: context imbalance in 'shrink_dcache_sb' - different lock contexts for basic block fs/dcache.c:1385:6: warning: context imbalance in 'd_delete' - wrong count at exit fs/dcache.c:1636:2: warning: context imbalance in '__d_unalias' - unexpected unlock fs/dcache.c:1735:2: warning: context imbalance in 'd_materialise_unique' - different lock contexts for basic block Signed-off-by: Miklos Szeredi Reviewed-by: Matthew Wilcox Acked-by: Christoph Hellwig Signed-off-by: Al Viro diff --git a/fs/dcache.c b/fs/dcache.c index 2b479de..e4b2b94 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -106,9 +107,10 @@ static void dentry_lru_remove(struct dentry *dentry) /* * Release the dentry's inode, using the filesystem * d_iput() operation if defined. - * Called with dcache_lock and per dentry lock held, drops both. */ static void dentry_iput(struct dentry * dentry) + __releases(dentry->d_lock) + __releases(dcache_lock) { struct inode *inode = dentry->d_inode; if (inode) { @@ -132,12 +134,13 @@ static void dentry_iput(struct dentry * dentry) * d_kill - kill dentry and return parent * @dentry: dentry to kill * - * Called with dcache_lock and d_lock, releases both. The dentry must - * already be unhashed and removed from the LRU. + * The dentry must already be unhashed and removed from the LRU. * * If this is the root of the dentry tree, return NULL. */ static struct dentry *d_kill(struct dentry *dentry) + __releases(dentry->d_lock) + __releases(dcache_lock) { struct dentry *parent; @@ -383,11 +386,11 @@ restart: * Try to prune ancestors as well. This is necessary to prevent * quadratic behavior of shrink_dcache_parent(), but is also expected * to be beneficial in reducing dentry cache fragmentation. - * - * Called with dcache_lock, drops it and then regains. - * Called with dentry->d_lock held, drops it. */ static void prune_one_dentry(struct dentry * dentry) + __releases(dentry->d_lock) + __releases(dcache_lock) + __acquires(dcache_lock) { __d_drop(dentry); dentry = d_kill(dentry); @@ -1604,10 +1607,9 @@ static int d_isparent(struct dentry *p1, struct dentry *p2) * * Note: If ever the locking in lock_rename() changes, then please * remember to update this too... - * - * On return, dcache_lock will have been unlocked. */ static struct dentry *__d_unalias(struct dentry *dentry, struct dentry *alias) + __releases(dcache_lock) { struct mutex *m1 = NULL, *m2 = NULL; struct dentry *ret; @@ -1743,7 +1745,6 @@ out_nolock: shouldnt_be_hashed: spin_unlock(&dcache_lock); BUG(); - goto shouldnt_be_hashed; } static int prepend(char **buffer, int *buflen, const char *str, @@ -1758,7 +1759,7 @@ static int prepend(char **buffer, int *buflen, const char *str, } /** - * d_path - return the path of a dentry + * __d_path - return the path of a dentry * @path: the dentry/vfsmount to report * @root: root vfsmnt/dentry (may be modified by this function) * @buffer: buffer to return value in @@ -1847,7 +1848,7 @@ Elong: * * Returns the buffer or an error code if the path was too long. * - * "buflen" should be positive. Caller holds the dcache_lock. + * "buflen" should be positive. */ char *d_path(const struct path *path, char *buf, int buflen) { -- cgit v0.10.2 From cdd16d0265c9234228fd37fbbad844d7e894b278 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 23 Jun 2008 18:11:53 +0200 Subject: [patch 2/3] vfs: dcache cleanups Comment from Al Viro: add prepend_name() wrapper. Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro diff --git a/fs/dcache.c b/fs/dcache.c index e4b2b94..6068c25 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1747,8 +1747,7 @@ shouldnt_be_hashed: BUG(); } -static int prepend(char **buffer, int *buflen, const char *str, - int namelen) +static int prepend(char **buffer, int *buflen, const char *str, int namelen) { *buflen -= namelen; if (*buflen < 0) @@ -1758,6 +1757,11 @@ static int prepend(char **buffer, int *buflen, const char *str, return 0; } +static int prepend_name(char **buffer, int *buflen, struct qstr *name) +{ + return prepend(buffer, buflen, name->name, name->len); +} + /** * __d_path - return the path of a dentry * @path: the dentry/vfsmount to report @@ -1780,8 +1784,8 @@ char *__d_path(const struct path *path, struct path *root, { struct dentry *dentry = path->dentry; struct vfsmount *vfsmnt = path->mnt; - char * end = buffer+buflen; - char * retval; + char *end = buffer + buflen; + char *retval; spin_lock(&vfsmount_lock); prepend(&end, &buflen, "\0", 1); @@ -1811,8 +1815,7 @@ char *__d_path(const struct path *path, struct path *root, } parent = dentry->d_parent; prefetch(parent); - if ((prepend(&end, &buflen, dentry->d_name.name, - dentry->d_name.len) != 0) || + if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) || (prepend(&end, &buflen, "/", 1) != 0)) goto Elong; retval = end; @@ -1825,8 +1828,7 @@ out: global_root: retval += 1; /* hit the slash */ - if (prepend(&retval, &buflen, dentry->d_name.name, - dentry->d_name.len) != 0) + if (prepend_name(&retval, &buflen, &dentry->d_name) != 0) goto Elong; root->mnt = vfsmnt; root->dentry = dentry; @@ -1918,16 +1920,11 @@ char *dentry_path(struct dentry *dentry, char *buf, int buflen) retval = end-1; *retval = '/'; - for (;;) { - struct dentry *parent; - if (IS_ROOT(dentry)) - break; + while (!IS_ROOT(dentry)) { + struct dentry *parent = dentry->d_parent; - parent = dentry->d_parent; prefetch(parent); - - if ((prepend(&end, &buflen, dentry->d_name.name, - dentry->d_name.len) != 0) || + if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) || (prepend(&end, &buflen, "/", 1) != 0)) goto Elong; @@ -1978,7 +1975,7 @@ asmlinkage long sys_getcwd(char __user *buf, unsigned long size) error = -ENOENT; /* Has the current directory has been unlinked? */ spin_lock(&dcache_lock); - if (pwd.dentry->d_parent == pwd.dentry || !d_unhashed(pwd.dentry)) { + if (IS_ROOT(pwd.dentry) || !d_unhashed(pwd.dentry)) { unsigned long len; struct path tmp = root; char * cwd; -- cgit v0.10.2 From e8183c2452041326c95258ecc7865b6fcd91c730 Mon Sep 17 00:00:00 2001 From: Tomas Janousek Date: Mon, 23 Jun 2008 15:12:35 +0200 Subject: udf: Fix regression in UDF anchor block detection In some cases it could happen that some block passed test in udf_check_anchor_block() even though udf_read_tagged() refused to read it later (e.g. because checksum was not correct). This patch makes udf_check_anchor_block() use udf_read_tagged() so that the checking is stricter. This fixes the regression (certain disks unmountable) caused by commit 423cf6dc04eb79d441bfda2b127bc4b57134b41d. Signed-off-by: Tomas Janousek Signed-off-by: Jan Kara diff --git a/fs/udf/super.c b/fs/udf/super.c index 7a5f69b..44cc702 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -682,38 +682,26 @@ static int udf_vrs(struct super_block *sb, int silent) /* * Check whether there is an anchor block in the given block */ -static int udf_check_anchor_block(struct super_block *sb, sector_t block, - bool varconv) +static int udf_check_anchor_block(struct super_block *sb, sector_t block) { - struct buffer_head *bh = NULL; - tag *t; + struct buffer_head *bh; uint16_t ident; - uint32_t location; - if (varconv) { - if (udf_fixed_to_variable(block) >= - sb->s_bdev->bd_inode->i_size >> sb->s_blocksize_bits) - return 0; - bh = sb_bread(sb, udf_fixed_to_variable(block)); - } - else - bh = sb_bread(sb, block); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV) && + udf_fixed_to_variable(block) >= + sb->s_bdev->bd_inode->i_size >> sb->s_blocksize_bits) + return 0; + bh = udf_read_tagged(sb, block, block, &ident); if (!bh) return 0; - - t = (tag *)bh->b_data; - ident = le16_to_cpu(t->tagIdent); - location = le32_to_cpu(t->tagLocation); brelse(bh); - if (ident != TAG_IDENT_AVDP) - return 0; - return location == block; + + return ident == TAG_IDENT_AVDP; } /* Search for an anchor volume descriptor pointer */ -static sector_t udf_scan_anchors(struct super_block *sb, bool varconv, - sector_t lastblock) +static sector_t udf_scan_anchors(struct super_block *sb, sector_t lastblock) { sector_t last[6]; int i; @@ -739,7 +727,7 @@ static sector_t udf_scan_anchors(struct super_block *sb, bool varconv, sb->s_blocksize_bits) continue; - if (udf_check_anchor_block(sb, last[i], varconv)) { + if (udf_check_anchor_block(sb, last[i])) { sbi->s_anchor[0] = last[i]; sbi->s_anchor[1] = last[i] - 256; return last[i]; @@ -748,17 +736,17 @@ static sector_t udf_scan_anchors(struct super_block *sb, bool varconv, if (last[i] < 256) continue; - if (udf_check_anchor_block(sb, last[i] - 256, varconv)) { + if (udf_check_anchor_block(sb, last[i] - 256)) { sbi->s_anchor[1] = last[i] - 256; return last[i]; } } - if (udf_check_anchor_block(sb, sbi->s_session + 256, varconv)) { + if (udf_check_anchor_block(sb, sbi->s_session + 256)) { sbi->s_anchor[0] = sbi->s_session + 256; return last[0]; } - if (udf_check_anchor_block(sb, sbi->s_session + 512, varconv)) { + if (udf_check_anchor_block(sb, sbi->s_session + 512)) { sbi->s_anchor[0] = sbi->s_session + 512; return last[0]; } @@ -780,23 +768,24 @@ static void udf_find_anchor(struct super_block *sb) int i; struct udf_sb_info *sbi = UDF_SB(sb); - lastblock = udf_scan_anchors(sb, 0, sbi->s_last_block); + lastblock = udf_scan_anchors(sb, sbi->s_last_block); if (lastblock) goto check_anchor; /* No anchor found? Try VARCONV conversion of block numbers */ + UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); /* Firstly, we try to not convert number of the last block */ - lastblock = udf_scan_anchors(sb, 1, + lastblock = udf_scan_anchors(sb, udf_variable_to_fixed(sbi->s_last_block)); - if (lastblock) { - UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); + if (lastblock) goto check_anchor; - } /* Secondly, we try with converted number of the last block */ - lastblock = udf_scan_anchors(sb, 1, sbi->s_last_block); - if (lastblock) - UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); + lastblock = udf_scan_anchors(sb, sbi->s_last_block); + if (!lastblock) { + /* VARCONV didn't help. Clear it. */ + UDF_CLEAR_FLAG(sb, UDF_FLAG_VARCONV); + } check_anchor: /* -- cgit v0.10.2 From c95e62ce8905aab62fed224eaaa9b8558a0ef652 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 23 Jun 2008 09:14:31 -0600 Subject: [SCSI] ses: Fix timeout Timeouts are measured in jiffies, not in seconds. Signed-off-by: Matthew Wilcox Cc: Stable Tree Signed-off-by: James Bottomley diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 45df83b..0fe031f 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -61,7 +61,7 @@ static int ses_probe(struct device *dev) return err; } -#define SES_TIMEOUT 30 +#define SES_TIMEOUT (30 * HZ) #define SES_RETRIES 3 static int ses_recv_diag(struct scsi_device *sdev, int page_code, -- cgit v0.10.2 From 88a6f4ad76be425f47df7f892baf913bcd466fb3 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 24 Jun 2008 13:30:45 -0700 Subject: netfilter: ip6table_mangle: don't reroute in LOCAL_IN Rerouting should only happen in LOCAL_OUT, in INPUT its useless since the packet has already chosen its final destination. Noticed by Alexey Dobriyan . Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index 27a5e8b..f405cea 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -129,7 +129,7 @@ static struct nf_hook_ops ip6t_ops[] __read_mostly = { .priority = NF_IP6_PRI_MANGLE, }, { - .hook = ip6t_local_hook, + .hook = ip6t_route_hook, .owner = THIS_MODULE, .pf = PF_INET6, .hooknum = NF_INET_LOCAL_IN, -- cgit v0.10.2 From eadc49b1a8d09480f14caea292142f103a89c77a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 17 Jun 2008 23:53:22 -0700 Subject: [SCSI] esp: Fix OOPS in esp_reset_cleanup(). OOPS reported by Friedrich Oslage The problem here is that tp->starget is set every time a lun is allocated for a particular target so we can catch the sdev_target parent value. The reset handler uses the NULL'ness of this value to determine which targets are active. But esp_slave_destroy() does not NULL out this value when appropriate. So for every target that doesn't respond, the SCSI bus scan causes a stale pointer to be left here, with ensuing crashes like you're seeing. Signed-off-by: David S. Miller Cc: Stable Tree Signed-off-by: James Bottomley diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c index a0b6d41..305edde 100644 --- a/drivers/scsi/esp_scsi.c +++ b/drivers/scsi/esp_scsi.c @@ -2371,6 +2371,7 @@ static int esp_slave_alloc(struct scsi_device *dev) dev->hostdata = lp; tp->starget = dev->sdev_target; + tp->starget_ref++; spi_min_period(tp->starget) = esp->min_period; spi_max_offset(tp->starget) = 15; @@ -2425,10 +2426,17 @@ static int esp_slave_configure(struct scsi_device *dev) static void esp_slave_destroy(struct scsi_device *dev) { + struct esp *esp = shost_priv(dev->host); + struct esp_target_data *tp = &esp->target[dev->id]; struct esp_lun_data *lp = dev->hostdata; kfree(lp); dev->hostdata = NULL; + + BUG_ON(tp->starget_ref <= 0); + + if (!--tp->starget_ref) + tp->starget = NULL; } static int esp_eh_abort_handler(struct scsi_cmnd *cmd) diff --git a/drivers/scsi/esp_scsi.h b/drivers/scsi/esp_scsi.h index bb43a13..655e0b2 100644 --- a/drivers/scsi/esp_scsi.h +++ b/drivers/scsi/esp_scsi.h @@ -322,6 +322,7 @@ struct esp_target_data { u8 nego_goal_tags; struct scsi_target *starget; + int starget_ref; }; struct esp_event_ent { -- cgit v0.10.2 From 13d5ef97f0675d789f559cfebc1df9d5e2b1879c Mon Sep 17 00:00:00 2001 From: Peng Haitao Date: Fri, 16 May 2008 10:15:04 +0800 Subject: [PATCH] kernel/audit.c: nlh->nlmsg_type is gotten more than once The first argument "nlh->nlmsg_type" of audit_receive_filter() should be modified to "msg_type" in audit_receive_msg(). Signed-off-by: Peng Haitao Signed-off-by: Al Viro diff --git a/kernel/audit.c b/kernel/audit.c index e8692a5..56f3028 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -779,7 +779,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) } /* fallthrough */ case AUDIT_LIST: - err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, + err = audit_receive_filter(msg_type, NETLINK_CB(skb).pid, uid, seq, data, nlmsg_len(nlh), loginuid, sessionid, sid); break; @@ -798,7 +798,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) } /* fallthrough */ case AUDIT_LIST_RULES: - err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, + err = audit_receive_filter(msg_type, NETLINK_CB(skb).pid, uid, seq, data, nlmsg_len(nlh), loginuid, sessionid, sid); break; -- cgit v0.10.2 From 9f0aecdd1cd6aacee9aa8f08031f4f2e09e454dc Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 19 May 2008 15:09:21 -0700 Subject: [PATCH] audit: fix kernel-doc parameter notation Fix auditfilter kernel-doc misssing parameter description: Warning(lin2626-rc3//kernel/auditfilter.c:1551): No description found for parameter 'sessionid' Signed-off-by: Randy Dunlap Signed-off-by: Al Viro diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 0e0bd27e..75cdf26 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1544,6 +1544,7 @@ static void audit_log_rule_change(uid_t loginuid, u32 sessionid, u32 sid, * @data: payload data * @datasz: size of payload data * @loginuid: loginuid of sender + * @sessionid: sessionid for netlink audit message * @sid: SE Linux Security ID of sender */ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, -- cgit v0.10.2 From d8de72473effd674a3c1fe9621821f406f5587c9 Mon Sep 17 00:00:00 2001 From: Peng Haitao Date: Tue, 20 May 2008 09:13:02 +0800 Subject: [PATCH] remove useless argument type in audit_filter_user() The second argument "type" is not used in audit_filter_user(), so I think that type can be removed. If I'm wrong, please tell me. Signed-off-by: Peng Haitao Signed-off-by: Al Viro diff --git a/include/linux/audit.h b/include/linux/audit.h index 63c3bb9..8b82974 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -571,7 +571,7 @@ extern void audit_log_lost(const char *message); extern int audit_update_lsm_rules(void); /* Private API (for audit.c only) */ -extern int audit_filter_user(struct netlink_skb_parms *cb, int type); +extern int audit_filter_user(struct netlink_skb_parms *cb); extern int audit_filter_type(int type); extern int audit_receive_filter(int type, int pid, int uid, int seq, void *data, size_t datasz, uid_t loginuid, diff --git a/kernel/audit.c b/kernel/audit.c index 56f3028..e092f1c 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -738,7 +738,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (!audit_enabled && msg_type != AUDIT_USER_AVC) return 0; - err = audit_filter_user(&NETLINK_CB(skb), msg_type); + err = audit_filter_user(&NETLINK_CB(skb)); if (err == 1) { err = 0; if (msg_type == AUDIT_USER_TTY) { diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 75cdf26..98c50cc 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1721,7 +1721,7 @@ static int audit_filter_user_rules(struct netlink_skb_parms *cb, return 1; } -int audit_filter_user(struct netlink_skb_parms *cb, int type) +int audit_filter_user(struct netlink_skb_parms *cb) { enum audit_state state = AUDIT_DISABLED; struct audit_entry *e; -- cgit v0.10.2 From 71a7d1556264c1ad84b1bcf5f7a9cbc980b1e1fd Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Wed, 25 Jun 2008 12:02:07 +0800 Subject: Blackfin arch: fix bug - kernel boot fails when Spinlock and rw-lock debugging enabled Initialize the lock of bad_irq_desc properly. The content of irq_desc array is replaced by bad_irq_desc in blackfin arch irqchip init code. So, do it properly as common irq init code. Signed-off-by: Sonic Zhang Signed-off-by: Bryan Wu diff --git a/arch/blackfin/kernel/irqchip.c b/arch/blackfin/kernel/irqchip.c index 73647c1..07402f5 100644 --- a/arch/blackfin/kernel/irqchip.c +++ b/arch/blackfin/kernel/irqchip.c @@ -60,9 +60,14 @@ static struct irq_chip bad_chip = { }; static struct irq_desc bad_irq_desc = { + .status = IRQ_DISABLED, .chip = &bad_chip, .handle_irq = handle_bad_irq, .depth = 1, + .lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock), +#ifdef CONFIG_SMP + .affinity = CPU_MASK_ALL +#endif }; int show_interrupts(struct seq_file *p, void *v) -- cgit v0.10.2 From 8d0a60032ffd472f0f7821ff388e9f438c06077a Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Wed, 25 Jun 2008 12:41:51 +0800 Subject: Blackfin arch: fix up section mismatch warning -- WARNING: vmlinux.o(.text+0x721a): Section mismatch in reference from the function ___fill_code_cplbtab() to the function .init.text:_fill_cplbtab() The function ___fill_code_cplbtab() references the function __init _fill_cplbtab(). This is often because ___fill_code_cplbtab lacks a __init annotation or the annotation of _fill_cplbtab is wrong. WARNING: vmlinux.o(.text+0x7238): Section mismatch in reference from the function ___fill_code_cplbtab() to the function .init.text:_fill_cplbtab() The function ___fill_code_cplbtab() references the function __init _fill_cplbtab(). This is often because ___fill_code_cplbtab lacks a __init annotation or the annotation of _fill_cplbtab is wrong. WARNING: vmlinux.o(.text+0x7250): Section mismatch in reference from the function ___fill_code_cplbtab() to the function .init.text:_fill_cplbtab() The function ___fill_code_cplbtab() references the function __init _fill_cplbtab(). This is often because ___fill_code_cplbtab lacks a __init annotation or the annotation of _fill_cplbtab is wrong. WARNING: vmlinux.o(.text+0x7264): Section mismatch in reference from the function ___fill_code_cplbtab() to the function .init.text:_fill_cplbtab() The function ___fill_code_cplbtab() references the function __init _fill_cplbtab(). This is often because ___fill_code_cplbtab lacks a __init annotation or the annotation of _fill_cplbtab is wrong. WARNING: vmlinux.o(.text+0x72a2): Section mismatch in reference from the function ___fill_data_cplbtab() to the function .init.text:_fill_cplbtab() The function ___fill_data_cplbtab() references the function __init _fill_cplbtab(). This is often because ___fill_data_cplbtab lacks a __init annotation or the annotation of _fill_cplbtab is wrong. WARNING: vmlinux.o(.text+0x72bc): Section mismatch in reference from the function ___fill_data_cplbtab() to the function .init.text:_fill_cplbtab() The function ___fill_data_cplbtab() references the function __init _fill_cplbtab(). This is often because ___fill_data_cplbtab lacks a __init annotation or the annotation of _fill_cplbtab is wrong. WARNING: vmlinux.o(.text+0x72d4): Section mismatch in reference from the function ___fill_data_cplbtab() to the function .init.text:_fill_cplbtab() The function ___fill_data_cplbtab() references the function __init _fill_cplbtab(). This is often because ___fill_data_cplbtab lacks a __init annotation or the annotation of _fill_cplbtab is wrong. WARNING: vmlinux.o(.text+0x72e8): Section mismatch in reference from the function ___fill_data_cplbtab() to the function .init.text:_fill_cplbtab() The function ___fill_data_cplbtab() references the function __init _fill_cplbtab(). This is often because ___fill_data_cplbtab lacks a __init annotation or the annotation of _fill_cplbtab is wrong. -- Signed-off-by: Bryan Wu diff --git a/arch/blackfin/kernel/cplb-nompu/cplbinit.c b/arch/blackfin/kernel/cplb-nompu/cplbinit.c index 917325b..6be0c50 100644 --- a/arch/blackfin/kernel/cplb-nompu/cplbinit.c +++ b/arch/blackfin/kernel/cplb-nompu/cplbinit.c @@ -254,7 +254,8 @@ close_cplbtab(struct cplb_tab *table) } /* helper function */ -static void __fill_code_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end) +static void __init +__fill_code_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end) { if (cplb_data[i].psize) { fill_cplbtab(t, @@ -291,7 +292,8 @@ static void __fill_code_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_en } } -static void __fill_data_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end) +static void __init +__fill_data_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end) { if (cplb_data[i].psize) { fill_cplbtab(t, -- cgit v0.10.2 From 59d393ad92f719d9ef36b96eae56d4817a7eeb10 Mon Sep 17 00:00:00 2001 From: Tony Vroon Date: Wed, 11 Jun 2008 16:23:56 -0400 Subject: mac80211: implement EU regulatory domain Implement missing EU regulatory domain for mac80211. Based on the information in IEEE 802.11-2007 (specifically pages 1142, 1143 & 1148) and ETSI 301 893 (V1.4.1). With thanks to Johannes Berg. Signed-off-by: Tony Vroon Acked-by: Johannes Berg Signed-off-by: John W. Linville diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 185488d..855bff4 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -80,6 +80,23 @@ static const struct ieee80211_channel_range ieee80211_JP_channels[] = { IEEE80211_CHAN_RADAR), }; +static const struct ieee80211_channel_range ieee80211_EU_channels[] = { + /* IEEE 802.11b/g, channels 1..13 */ + RANGE_PWR(2412, 2472, 20, 6, 0), + /* IEEE 802.11a, channel 36*/ + RANGE_PWR(5180, 5180, 23, 6, IEEE80211_CHAN_PASSIVE_SCAN), + /* IEEE 802.11a, channel 40*/ + RANGE_PWR(5200, 5200, 23, 6, IEEE80211_CHAN_PASSIVE_SCAN), + /* IEEE 802.11a, channel 44*/ + RANGE_PWR(5220, 5220, 23, 6, IEEE80211_CHAN_PASSIVE_SCAN), + /* IEEE 802.11a, channels 48..64 */ + RANGE_PWR(5240, 5320, 23, 6, IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_RADAR), + /* IEEE 802.11a, channels 100..140 */ + RANGE_PWR(5500, 5700, 30, 6, IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_RADAR), +}; + #define REGDOM(_code) \ { \ .code = __stringify(_code), \ @@ -90,6 +107,7 @@ static const struct ieee80211_channel_range ieee80211_JP_channels[] = { static const struct ieee80211_regdomain ieee80211_regdoms[] = { REGDOM(US), REGDOM(JP), + REGDOM(EU), }; -- cgit v0.10.2 From c9e8eae0935f03e2d03a7ad7af80d8fc6c53e68c Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Sun, 15 Jun 2008 15:17:29 +0200 Subject: b43: Do not return TX_BUSY from op_tx Never return TX_BUSY from op_tx. It doesn't make sense to return TX_BUSY, if we can not transmit the packet. Drop the packet and return TX_OK. This will fix the resume hang. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index fa4b0d8..a708277 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -2883,12 +2883,11 @@ static int b43_op_tx(struct ieee80211_hw *hw, if (unlikely(skb->len < 2 + 2 + 6)) { /* Too short, this can't be a valid frame. */ - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; + goto drop_packet; } B43_WARN_ON(skb_shinfo(skb)->nr_frags); if (unlikely(!dev)) - return NETDEV_TX_BUSY; + goto drop_packet; /* Transmissions on seperate queues can run concurrently. */ read_lock_irqsave(&wl->tx_lock, flags); @@ -2904,7 +2903,12 @@ static int b43_op_tx(struct ieee80211_hw *hw, read_unlock_irqrestore(&wl->tx_lock, flags); if (unlikely(err)) - return NETDEV_TX_BUSY; + goto drop_packet; + return NETDEV_TX_OK; + +drop_packet: + /* We can not transmit this packet. Drop it. */ + dev_kfree_skb_any(skb); return NETDEV_TX_OK; } -- cgit v0.10.2 From 664f200610a3c9641ff58fc91b986b804cb1cc2d Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Sun, 15 Jun 2008 15:27:49 +0200 Subject: b43legacy: Do not return TX_BUSY from op_tx Never return TX_BUSY from op_tx. It doesn't make sense to return TX_BUSY, if we can not transmit the packet. Drop the packet and return TX_OK. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 204077c..3e612d0 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2378,8 +2378,10 @@ static int b43legacy_op_tx(struct ieee80211_hw *hw, } else err = b43legacy_dma_tx(dev, skb, ctl); out: - if (unlikely(err)) - return NETDEV_TX_BUSY; + if (unlikely(err)) { + /* Drop the packet. */ + dev_kfree_skb_any(skb); + } return NETDEV_TX_OK; } -- cgit v0.10.2 From 7b3abfc87ec13a81b255012b6e1bd4caeeb05aec Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Sun, 15 Jun 2008 16:01:24 +0200 Subject: b43: Fix possible MMIO access while device is down This fixes a possible MMIO access while the device is still down from a suspend cycle. MMIO accesses with the device powered down may cause crashes on certain devices. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c index 36a9c42..76f4c7b 100644 --- a/drivers/net/wireless/b43/leds.c +++ b/drivers/net/wireless/b43/leds.c @@ -72,6 +72,9 @@ static void b43_led_brightness_set(struct led_classdev *led_dev, struct b43_wldev *dev = led->dev; bool radio_enabled; + if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) + return; + /* Checking the radio-enabled status here is slightly racy, * but we want to avoid the locking overhead and we don't care * whether the LED has the wrong state for a second. */ -- cgit v0.10.2 From 2f9ec47d0954f9d2e5a00209c2689cbc477a8c89 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Fri, 20 Jun 2008 11:40:46 +0200 Subject: b43legacy: Fix possible NULL pointer dereference in DMA code This fixes a possible NULL pointer dereference in an error path of the DMA allocation error checking code. This is also necessary for a future DMA API change that is on its way into the mainline kernel that adds an additional dev parameter to dma_mapping_error(). Signed-off-by: Michael Buesch Cc: stable Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c index c990f87..93ddc1c 100644 --- a/drivers/net/wireless/b43legacy/dma.c +++ b/drivers/net/wireless/b43legacy/dma.c @@ -876,6 +876,7 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, if (!ring) goto out; ring->type = type; + ring->dev = dev; nr_slots = B43legacy_RXRING_SLOTS; if (for_tx) @@ -922,7 +923,6 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, DMA_TO_DEVICE); } - ring->dev = dev; ring->nr_slots = nr_slots; ring->mmio_base = b43legacy_dmacontroller_base(type, controller_index); ring->index = controller_index; -- cgit v0.10.2 From 99ade2597e3f7f0ad463c489aaccd6cc605e242c Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Fri, 20 Jun 2008 22:11:00 +0200 Subject: rt2x00: Fix unbalanced mutex locking The usb_cache_mutex was not correctly released under all circumstances. Both rt73usb as rt2500usb didn't release the mutex under certain conditions when the register access failed. Obviously such failure would lead to deadlocks. In addition under similar circumstances when the bbp register couldn't be read the value must be set to 0xff to indicate that the value is wrong. This too didn't happen under all circumstances. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index fdbd0ef..61e59c1 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -138,11 +138,8 @@ static void rt2500usb_bbp_write(struct rt2x00_dev *rt2x00dev, * Wait until the BBP becomes ready. */ reg = rt2500usb_bbp_check(rt2x00dev); - if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR8 register busy. Write failed.\n"); - mutex_unlock(&rt2x00dev->usb_cache_mutex); - return; - } + if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) + goto exit_fail; /* * Write the data into the BBP. @@ -155,6 +152,13 @@ static void rt2500usb_bbp_write(struct rt2x00_dev *rt2x00dev, rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg); mutex_unlock(&rt2x00dev->usb_cache_mutex); + + return; + +exit_fail: + mutex_unlock(&rt2x00dev->usb_cache_mutex); + + ERROR(rt2x00dev, "PHY_CSR8 register busy. Write failed.\n"); } static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev, @@ -168,10 +172,8 @@ static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev, * Wait until the BBP becomes ready. */ reg = rt2500usb_bbp_check(rt2x00dev); - if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR8 register busy. Read failed.\n"); - return; - } + if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) + goto exit_fail; /* * Write the request into the BBP. @@ -186,17 +188,21 @@ static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev, * Wait until the BBP becomes ready. */ reg = rt2500usb_bbp_check(rt2x00dev); - if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR8 register busy. Read failed.\n"); - *value = 0xff; - mutex_unlock(&rt2x00dev->usb_cache_mutex); - return; - } + if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) + goto exit_fail; rt2500usb_register_read_lock(rt2x00dev, PHY_CSR7, ®); *value = rt2x00_get_field16(reg, PHY_CSR7_DATA); mutex_unlock(&rt2x00dev->usb_cache_mutex); + + return; + +exit_fail: + mutex_unlock(&rt2x00dev->usb_cache_mutex); + + ERROR(rt2x00dev, "PHY_CSR8 register busy. Read failed.\n"); + *value = 0xff; } static void rt2500usb_rf_write(struct rt2x00_dev *rt2x00dev, diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index fff8386e..83cc014 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -134,11 +134,8 @@ static void rt73usb_bbp_write(struct rt2x00_dev *rt2x00dev, * Wait until the BBP becomes ready. */ reg = rt73usb_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR3 register busy. Write failed.\n"); - mutex_unlock(&rt2x00dev->usb_cache_mutex); - return; - } + if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) + goto exit_fail; /* * Write the data into the BBP. @@ -151,6 +148,13 @@ static void rt73usb_bbp_write(struct rt2x00_dev *rt2x00dev, rt73usb_register_write_lock(rt2x00dev, PHY_CSR3, reg); mutex_unlock(&rt2x00dev->usb_cache_mutex); + + return; + +exit_fail: + mutex_unlock(&rt2x00dev->usb_cache_mutex); + + ERROR(rt2x00dev, "PHY_CSR3 register busy. Write failed.\n"); } static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev, @@ -164,11 +168,8 @@ static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev, * Wait until the BBP becomes ready. */ reg = rt73usb_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n"); - mutex_unlock(&rt2x00dev->usb_cache_mutex); - return; - } + if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) + goto exit_fail; /* * Write the request into the BBP. @@ -184,14 +185,19 @@ static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev, * Wait until the BBP becomes ready. */ reg = rt73usb_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n"); - *value = 0xff; - return; - } + if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) + goto exit_fail; *value = rt2x00_get_field32(reg, PHY_CSR3_VALUE); mutex_unlock(&rt2x00dev->usb_cache_mutex); + + return; + +exit_fail: + mutex_unlock(&rt2x00dev->usb_cache_mutex); + + ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n"); + *value = 0xff; } static void rt73usb_rf_write(struct rt2x00_dev *rt2x00dev, -- cgit v0.10.2 From 66b5004d85164a6439d3ba1e7757734472ee2cac Mon Sep 17 00:00:00 2001 From: Ron Rindjunsky Date: Wed, 25 Jun 2008 16:46:31 +0800 Subject: iwlwifi: improve scanning band selection management This patch modifies the band selection management when scanning, so bands are now scanned according to HW band support. Signed-off-by: Ron Rindjunsky Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 13925b6..b1b3c52 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2227,7 +2227,10 @@ static int iwl3945_scan_initiate(struct iwl3945_priv *priv) } IWL_DEBUG_INFO("Starting scan...\n"); - priv->scan_bands = 2; + if (priv->cfg->sku & IWL_SKU_G) + priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ); + if (priv->cfg->sku & IWL_SKU_A) + priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ); set_bit(STATUS_SCANNING, &priv->status); priv->scan_start = jiffies; priv->scan_pass_start = priv->scan_start; @@ -3352,13 +3355,18 @@ static void iwl3945_rx_scan_complete_notif(struct iwl3945_priv *priv, cancel_delayed_work(&priv->scan_check); IWL_DEBUG_INFO("Scan pass on %sGHz took %dms\n", - (priv->scan_bands == 2) ? "2.4" : "5.2", + (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) ? + "2.4" : "5.2", jiffies_to_msecs(elapsed_jiffies (priv->scan_pass_start, jiffies))); - /* Remove this scanned band from the list - * of pending bands to scan */ - priv->scan_bands--; + /* Remove this scanned band from the list of pending + * bands to scan, band G precedes A in order of scanning + * as seen in iwl3945_bg_request_scan */ + if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) + priv->scan_bands &= ~BIT(IEEE80211_BAND_2GHZ); + else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) + priv->scan_bands &= ~BIT(IEEE80211_BAND_5GHZ); /* If a request to abort was given, or the scan did not succeed * then we reset the scan state machine and terminate, @@ -4972,7 +4980,7 @@ static int iwl3945_get_channels_for_scan(struct iwl3945_priv *priv, ch_info = iwl3945_get_channel_info(priv, band, scan_ch->channel); if (!is_channel_valid(ch_info)) { - IWL_DEBUG_SCAN("Channel %d is INVALID for this SKU.\n", + IWL_DEBUG_SCAN("Channel %d is INVALID for this band.\n", scan_ch->channel); continue; } @@ -6315,21 +6323,16 @@ static void iwl3945_bg_request_scan(struct work_struct *data) /* flags + rate selection */ - switch (priv->scan_bands) { - case 2: + if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) { scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; scan->tx_cmd.rate = IWL_RATE_1M_PLCP; scan->good_CRC_th = 0; band = IEEE80211_BAND_2GHZ; - break; - - case 1: + } else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) { scan->tx_cmd.rate = IWL_RATE_6M_PLCP; scan->good_CRC_th = IWL_GOOD_CRC_TH; band = IEEE80211_BAND_5GHZ; - break; - - default: + } else { IWL_WARNING("Invalid scan band count\n"); goto done; } @@ -6770,7 +6773,7 @@ static int iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co ch_info = iwl3945_get_channel_info(priv, conf->channel->band, conf->channel->hw_value); if (!is_channel_valid(ch_info)) { - IWL_DEBUG_SCAN("Channel %d [%d] is INVALID for this SKU.\n", + IWL_DEBUG_SCAN("Channel %d [%d] is INVALID for this band.\n", conf->channel->hw_value, conf->channel->band); IWL_DEBUG_MAC80211("leave - invalid channel\n"); spin_unlock_irqrestore(&priv->lock, flags); diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index 883b42f..5ed16ce 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -1774,7 +1774,10 @@ static int iwl4965_scan_initiate(struct iwl_priv *priv) } IWL_DEBUG_INFO("Starting scan...\n"); - priv->scan_bands = 2; + if (priv->cfg->sku & IWL_SKU_G) + priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ); + if (priv->cfg->sku & IWL_SKU_A) + priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ); set_bit(STATUS_SCANNING, &priv->status); priv->scan_start = jiffies; priv->scan_pass_start = priv->scan_start; @@ -3023,8 +3026,9 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index); if (index != -1) { - int freed = iwl4965_tx_queue_reclaim(priv, txq_id, index); #ifdef CONFIG_IWL4965_HT + int freed = iwl4965_tx_queue_reclaim(priv, txq_id, index); + if (tid != MAX_TID_COUNT) priv->stations[sta_id].tid[tid].tfds_in_queue -= freed; if (iwl4965_queue_space(&txq->q) > txq->q.low_mark && @@ -3276,13 +3280,18 @@ static void iwl4965_rx_scan_complete_notif(struct iwl_priv *priv, cancel_delayed_work(&priv->scan_check); IWL_DEBUG_INFO("Scan pass on %sGHz took %dms\n", - (priv->scan_bands == 2) ? "2.4" : "5.2", + (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) ? + "2.4" : "5.2", jiffies_to_msecs(elapsed_jiffies (priv->scan_pass_start, jiffies))); - /* Remove this scanned band from the list - * of pending bands to scan */ - priv->scan_bands--; + /* Remove this scanned band from the list of pending + * bands to scan, band G precedes A in order of scanning + * as seen in iwl_bg_request_scan */ + if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) + priv->scan_bands &= ~BIT(IEEE80211_BAND_2GHZ); + else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) + priv->scan_bands &= ~BIT(IEEE80211_BAND_5GHZ); /* If a request to abort was given, or the scan did not succeed * then we reset the scan state machine and terminate, @@ -3292,7 +3301,7 @@ static void iwl4965_rx_scan_complete_notif(struct iwl_priv *priv, clear_bit(STATUS_SCAN_ABORTING, &priv->status); } else { /* If there are more bands on this scan pass reschedule */ - if (priv->scan_bands > 0) + if (priv->scan_bands) goto reschedule; } @@ -4635,10 +4644,9 @@ static int iwl4965_get_channels_for_scan(struct iwl_priv *priv, scan_ch->channel = ieee80211_frequency_to_channel(channels[i].center_freq); - ch_info = iwl_get_channel_info(priv, band, - scan_ch->channel); + ch_info = iwl_get_channel_info(priv, band, scan_ch->channel); if (!is_channel_valid(ch_info)) { - IWL_DEBUG_SCAN("Channel %d is INVALID for this SKU.\n", + IWL_DEBUG_SCAN("Channel %d is INVALID for this band.\n", scan_ch->channel); continue; } @@ -5830,8 +5838,7 @@ static void iwl4965_bg_request_scan(struct work_struct *data) scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - switch (priv->scan_bands) { - case 2: + if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) { scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; scan->tx_cmd.rate_n_flags = iwl4965_hw_set_rate_n_flags(IWL_RATE_1M_PLCP, @@ -5839,17 +5846,13 @@ static void iwl4965_bg_request_scan(struct work_struct *data) scan->good_CRC_th = 0; band = IEEE80211_BAND_2GHZ; - break; - - case 1: + } else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) { scan->tx_cmd.rate_n_flags = iwl4965_hw_set_rate_n_flags(IWL_RATE_6M_PLCP, RATE_MCS_ANT_B_MSK); scan->good_CRC_th = IWL_GOOD_CRC_TH; band = IEEE80211_BAND_5GHZ; - break; - - default: + } else { IWL_WARNING("Invalid scan band count\n"); goto done; } -- cgit v0.10.2 From ec5e69f6d3f4350681d6f7eaae515cf014be9276 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Mon, 23 Jun 2008 14:52:09 -0500 Subject: [SCSI] esp: tidy up target reference counting The esp driver currently does hand rolled reference counting of its target. It's much easier to do what it needs to do if it's plugged into the mid-layer callbacks (target_alloc and target_destroy) which were designed for this case, so do it this way and get rid of the internal target reference count. Acked-by: David S. Miller Cc: Stable Tree Signed-off-by: James Bottomley diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c index 305edde..59fbef0 100644 --- a/drivers/scsi/esp_scsi.c +++ b/drivers/scsi/esp_scsi.c @@ -2359,6 +2359,24 @@ void scsi_esp_unregister(struct esp *esp) } EXPORT_SYMBOL(scsi_esp_unregister); +static int esp_target_alloc(struct scsi_target *starget) +{ + struct esp *esp = shost_priv(dev_to_shost(&starget->dev)); + struct esp_target_data *tp = &esp->target[starget->id]; + + tp->starget = starget; + + return 0; +} + +static void esp_target_destroy(struct scsi_target *starget) +{ + struct esp *esp = shost_priv(dev_to_shost(&starget->dev)); + struct esp_target_data *tp = &esp->target[starget->id]; + + tp->starget = NULL; +} + static int esp_slave_alloc(struct scsi_device *dev) { struct esp *esp = shost_priv(dev->host); @@ -2370,9 +2388,6 @@ static int esp_slave_alloc(struct scsi_device *dev) return -ENOMEM; dev->hostdata = lp; - tp->starget = dev->sdev_target; - tp->starget_ref++; - spi_min_period(tp->starget) = esp->min_period; spi_max_offset(tp->starget) = 15; @@ -2426,17 +2441,10 @@ static int esp_slave_configure(struct scsi_device *dev) static void esp_slave_destroy(struct scsi_device *dev) { - struct esp *esp = shost_priv(dev->host); - struct esp_target_data *tp = &esp->target[dev->id]; struct esp_lun_data *lp = dev->hostdata; kfree(lp); dev->hostdata = NULL; - - BUG_ON(tp->starget_ref <= 0); - - if (!--tp->starget_ref) - tp->starget = NULL; } static int esp_eh_abort_handler(struct scsi_cmnd *cmd) @@ -2616,6 +2624,8 @@ struct scsi_host_template scsi_esp_template = { .name = "esp", .info = esp_info, .queuecommand = esp_queuecommand, + .target_alloc = esp_target_alloc, + .target_destroy = esp_target_destroy, .slave_alloc = esp_slave_alloc, .slave_configure = esp_slave_configure, .slave_destroy = esp_slave_destroy, diff --git a/drivers/scsi/esp_scsi.h b/drivers/scsi/esp_scsi.h index 655e0b2..bb43a13 100644 --- a/drivers/scsi/esp_scsi.h +++ b/drivers/scsi/esp_scsi.h @@ -322,7 +322,6 @@ struct esp_target_data { u8 nego_goal_tags; struct scsi_target *starget; - int starget_ref; }; struct esp_event_ent { -- cgit v0.10.2 From 9e6c29768fc05d32ba1342b9348957b24bc476c2 Mon Sep 17 00:00:00 2001 From: Jaya Kumar Date: Sun, 22 Jun 2008 04:27:27 +0100 Subject: [ARM] 5117/1: pxafb: fix __devinit/exit annotations This patch fixes pxafb's init/exit annotations. It uses __devinit/exit for probe functions and __init for init functions. g_options is left as __devinitdata since it is used by both. Signed-off-by: Jaya Kumar Acked-by: Krzysztof Helt Acked-by: Eric Miao Signed-off-by: Russell King diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 796e0d1..fafe7db 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -1246,7 +1246,7 @@ static int pxafb_resume(struct platform_device *dev) * cache. Once this area is remapped, all virtual memory * access to the video memory should occur at the new region. */ -static int __init pxafb_map_video_memory(struct pxafb_info *fbi) +static int __devinit pxafb_map_video_memory(struct pxafb_info *fbi) { /* * We reserve one page for the palette, plus the size @@ -1348,7 +1348,7 @@ decode_mode: pxafb_decode_mode_info(fbi, inf->modes, inf->num_modes); } -static struct pxafb_info * __init pxafb_init_fbinfo(struct device *dev) +static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev) { struct pxafb_info *fbi; void *addr; @@ -1410,7 +1410,7 @@ static struct pxafb_info * __init pxafb_init_fbinfo(struct device *dev) } #ifdef CONFIG_FB_PXA_PARAMETERS -static int __init parse_opt_mode(struct device *dev, const char *this_opt) +static int __devinit parse_opt_mode(struct device *dev, const char *this_opt) { struct pxafb_mach_info *inf = dev->platform_data; @@ -1469,7 +1469,7 @@ done: return 0; } -static int __init parse_opt(struct device *dev, char *this_opt) +static int __devinit parse_opt(struct device *dev, char *this_opt) { struct pxafb_mach_info *inf = dev->platform_data; struct pxafb_mode_info *mode = &inf->modes[0]; @@ -1567,7 +1567,7 @@ static int __init parse_opt(struct device *dev, char *this_opt) return 0; } -static int __init pxafb_parse_options(struct device *dev, char *options) +static int __devinit pxafb_parse_options(struct device *dev, char *options) { char *this_opt; int ret; @@ -1589,7 +1589,7 @@ static int __init pxafb_parse_options(struct device *dev, char *options) static char g_options[256] __devinitdata = ""; #ifndef MODULE -static int __devinit pxafb_setup_options(void) +static int __init pxafb_setup_options(void) { char *options = NULL; @@ -1613,7 +1613,7 @@ MODULE_PARM_DESC(options, "LCD parameters (see Documentation/fb/pxafb.txt)"); #define pxafb_setup_options() (0) #endif -static int __init pxafb_probe(struct platform_device *dev) +static int __devinit pxafb_probe(struct platform_device *dev) { struct pxafb_info *fbi; struct pxafb_mach_info *inf; @@ -1801,7 +1801,7 @@ static struct platform_driver pxafb_driver = { }, }; -static int __devinit pxafb_init(void) +static int __init pxafb_init(void) { if (pxafb_setup_options()) return -EINVAL; -- cgit v0.10.2 From 16d752397301b95abaa95cbaf9e785d221872311 Mon Sep 17 00:00:00 2001 From: Rene Herman Date: Tue, 24 Jun 2008 19:38:56 +0200 Subject: thermal: Create CONFIG_THERMAL_HWMON=n A bug in libsensors <= 2.10.6 is exposed when this new hwmon I/F is enabled. Create CONFIG_THERMAL_HWMON=n until some time after libsensors 2.10.7 ships so those users can run the latest kernel. libsensors 3.x is already fixed -- those users can use CONFIG_THERMAL_HWMON=y now. Signed-off-by: Rene Herman Acked-by: Mark M. Hoffman Signed-off-by: Len Brown diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 5b3f31f..46ece3f 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -312,3 +312,12 @@ When: 2.6.26 Why: Implementation became generic; users should now include linux/semaphore.h instead. Who: Matthew Wilcox + +--------------------------- + +What: CONFIG_THERMAL_HWMON +When: January 2009 +Why: This option was introduced just to allow older lm-sensors userspace + to keep working over the upgrade to 2.6.26. At the scheduled time of + removal fixed lm-sensors (2.x or 3.x) should be readily available. +Who: Rene Herman diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 4b62852..a86e952 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -12,3 +12,12 @@ menuconfig THERMAL cooling devices. All platforms with ACPI thermal support can use this driver. If you want this support, you should say Y or M here. + +config THERMAL_HWMON + bool "Hardware monitoring support" + depends on HWMON=y || HWMON=THERMAL + help + The generic thermal sysfs driver's hardware monitoring support + requires a 2.10.7/3.0.2 or later lm-sensors userspace. + + Say Y if your user-space is new enough. diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 6098787..fe07462 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -295,8 +295,8 @@ thermal_cooling_device_trip_point_show(struct device *dev, /* Device management */ -#if defined(CONFIG_HWMON) || \ - (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE)) +#if defined(CONFIG_THERMAL_HWMON) + /* hwmon sys I/F */ #include static LIST_HEAD(thermal_hwmon_list); diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 06d3e6e..917707e 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -66,8 +66,7 @@ struct thermal_cooling_device { ((long)t-2732+5)/10 : ((long)t-2732-5)/10) #define CELSIUS_TO_KELVIN(t) ((t)*10+2732) -#if defined(CONFIG_HWMON) || \ - (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE)) +#if defined(CONFIG_THERMAL_HWMON) /* thermal zone devices with the same type share one hwmon device */ struct thermal_hwmon_device { char type[THERMAL_NAME_LENGTH]; @@ -94,8 +93,7 @@ struct thermal_zone_device { struct idr idr; struct mutex lock; /* protect cooling devices list */ struct list_head node; -#if defined(CONFIG_HWMON) || \ - (defined(CONFIG_HWMON_MODULE) && defined(CONFIG_THERMAL_MODULE)) +#if defined(CONFIG_THERMAL_HWMON) struct list_head hwmon_node; struct thermal_hwmon_device *hwmon; struct thermal_hwmon_attr temp_input; /* hwmon sys attr */ -- cgit v0.10.2 From 4389ed2ff61de2b1485b31bf342da913dd342f59 Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Fri, 20 Jun 2008 15:39:09 +0200 Subject: ACPI: don't walk tables if ACPI was disabled Ingo Molnar wrote: > -tip auto-testing started triggering this spinlock corruption message > yesterday: > > [ 3.976213] calling acpi_rtc_init+0x0/0xd3 > [ 3.980213] ACPI Exception (utmutex-0263): AE_BAD_PARAMETER, Thread F7C50000 could not acquire Mutex [3] [20080321] > [ 3.992213] BUG: spinlock bad magic on CPU#0, swapper/1 > [ 3.992213] lock: c2508dc4, .magic: 00000000, .owner: swapper/1, .owner_cpu: 0 This is apparently because some parts of ACPI, including mutexes, are not initialized when acpi=off is passed to the kernel. Reported-by: Ingo Molnar Signed-off-by: Vegard Nossum Signed-off-by: Len Brown diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 2808dc6..9b227d4 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -333,6 +333,9 @@ static int __init acpi_rtc_init(void) { struct device *dev = get_rtc_dev(); + if (acpi_disabled) + return 0; + if (dev) { rtc_wake_setup(); rtc_info.wake_on = rtc_wake_on; -- cgit v0.10.2 From 816c2eda3ce8fa7eb62f22e01e2ec7a3f7d677c0 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Tue, 24 Jun 2008 22:57:12 -0400 Subject: dock: bay: Don't call acpi_walk_namespace() when ACPI is disabled. Signed-off-by: Len Brown diff --git a/drivers/acpi/bay.c b/drivers/acpi/bay.c index 26038c2..61b6c5b 100644 --- a/drivers/acpi/bay.c +++ b/drivers/acpi/bay.c @@ -377,6 +377,9 @@ static int __init bay_init(void) INIT_LIST_HEAD(&drive_bays); + if (acpi_disabled) + return -ENODEV; + /* look for dockable drive bays */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, find_bay, &bays, NULL); diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 96c542f..bb7c51f 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -917,6 +917,9 @@ static int __init dock_init(void) dock_station = NULL; + if (acpi_disabled) + return 0; + /* look for a dock station */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, find_dock, &num, NULL); -- cgit v0.10.2 From 41aefdcc98fdba47459eab67630293d67e855fc3 Mon Sep 17 00:00:00 2001 From: Max Asbock Date: Wed, 25 Jun 2008 14:45:28 -0700 Subject: x86: shift bits the right way in native_read_tscp native_read_tscp shifts the bits in the high order value in the wrong direction, the attached patch fixes that. Signed-off-by: Max Asbock Acked-by: Glauber Costa Signed-off-by: Ingo Molnar diff --git a/include/asm-x86/msr.h b/include/asm-x86/msr.h index 3707650..2b5f2c9 100644 --- a/include/asm-x86/msr.h +++ b/include/asm-x86/msr.h @@ -18,7 +18,7 @@ static inline unsigned long long native_read_tscp(unsigned int *aux) unsigned long low, high; asm volatile(".byte 0x0f,0x01,0xf9" : "=a" (low), "=d" (high), "=c" (*aux)); - return low | ((u64)high >> 32); + return low | ((u64)high << 32); } /* -- cgit v0.10.2 From 0b1faeef5f9243bb5fc5713a34bbf1ceab0de562 Mon Sep 17 00:00:00 2001 From: Daniel J Blueman Date: Sun, 15 Jun 2008 12:32:15 +0100 Subject: x86: section/warning fixes WARNING: arch/x86/mm/built-in.o(.text+0x3a1): Section mismatch in reference from the function set_pte_phys() to the function .init.text:spp_getpage() The function set_pte_phys() references the function __init spp_getpage(). This is often because set_pte_phys lacks a __init annotation or the annotation of spp_getpage is wrong. arch/x86/mm/init_64.c: In function 'early_memtest': arch/x86/mm/init_64.c:520: warning: passing argument 2 of 'find_e820_area_size' from incompatible pointer type Signed-off-by: Daniel J Blueman Cc: "Linus Torvalds" Signed-off-by: Ingo Molnar diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 156e6d7..f6d20be 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -135,7 +135,7 @@ static __init void *spp_getpage(void) return ptr; } -static void +static __init void set_pte_phys(unsigned long vaddr, unsigned long phys, pgprot_t prot) { pgd_t *pgd; @@ -214,7 +214,7 @@ void __init cleanup_highmap(void) } /* NOTE: this is meant to be run only at boot */ -void __set_fixmap(enum fixed_addresses idx, unsigned long phys, pgprot_t prot) +void __init __set_fixmap(enum fixed_addresses idx, unsigned long phys, pgprot_t prot) { unsigned long address = __fix_to_virt(idx); @@ -506,7 +506,7 @@ early_param("memtest", parse_memtest); static void __init early_memtest(unsigned long start, unsigned long end) { - u64 t_start, t_size; + unsigned long t_start, t_size; unsigned pattern; if (!memtest_pattern) @@ -525,7 +525,7 @@ static void __init early_memtest(unsigned long start, unsigned long end) if (t_start + t_size > end) t_size = end - t_start; - printk(KERN_CONT "\n %016llx - %016llx pattern %d", + printk(KERN_CONT "\n %016lx - %016lx pattern %d", t_start, t_start + t_size, pattern); memtest(t_start, t_size, pattern); -- cgit v0.10.2 From fde60748d2f0345cabff20d25458c67d4ac06034 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 5 Jun 2008 18:59:08 -0300 Subject: V4L/DVB (8004): Fix INPUT dependency at budget-ci As reported by Ingo Molnar: MODPOST 346 modules ERROR: "input_free_device" [drivers/media/dvb/ttpci/budget-ci.ko] undefined! ERROR: "input_register_device" [drivers/media/dvb/ttpci/budget-ci.ko] undefined! ERROR: "input_allocate_device" [drivers/media/dvb/ttpci/budget-ci.ko] undefined! ERROR: "input_unregister_device" [drivers/media/dvb/ttpci/budget-ci.ko] undefined! ERROR: "input_event" [drivers/media/common/ir-common.ko] undefined! This occurs when: CONFIG_INPUT=n CONFIG_VIDEO_IR=m CONFIG_DVB_BUDGET_CI=m Thanks-to: Ingo Molnar Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index d4339b1..07643e0 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -101,6 +101,7 @@ config DVB_BUDGET config DVB_BUDGET_CI tristate "Budget cards with onboard CI connector" depends on DVB_BUDGET_CORE && I2C + depends on INPUT # due to IR select DVB_STV0297 if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE -- cgit v0.10.2 From bf67cac1314ba29676fbac2decde0e2e0a8170f8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 7 Jun 2008 15:54:10 -0300 Subject: V4L/DVB (8005): Fix OOPS if frontend is null Thanks to timf and Mike Galbraith to report this issue. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 469f93a..726f01c 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -1338,7 +1338,8 @@ static int dvb_init(struct saa7134_dev *dev) return ret; dettach_frontend: - dvb_frontend_detach(dev->dvb.frontend); + if (dev->dvb.frontend) + dvb_frontend_detach(dev->dvb.frontend); dev->dvb.frontend = NULL; return -1; -- cgit v0.10.2 From 45270a1531a2aa90dd890666913c25a1e6f5e8eb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 7 Jun 2008 11:18:17 -0300 Subject: V4L/DVB (8007): cx18/cx25840: the S-Video LUMA input can use all In1-In8 inputs The S-Video LUMA input was restricted to the In1-In4 inputs, but it turns out that it can use the full range of In1-In8. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 9a26751..8f2959a 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -228,7 +228,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, if ((vid_input & ~0xff0) || luma < CX18_AV_SVIDEO_LUMA1 || - luma > CX18_AV_SVIDEO_LUMA4 || + luma > CX18_AV_SVIDEO_LUMA8 || chroma < CX18_AV_SVIDEO_CHROMA4 || chroma > CX18_AV_SVIDEO_CHROMA8) { CX18_ERR("0x%04x is not a valid video input!\n", diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h index 786901d..39f3c93 100644 --- a/drivers/media/video/cx18/cx18-av-core.h +++ b/drivers/media/video/cx18/cx18-av-core.h @@ -37,12 +37,16 @@ enum cx18_av_video_input { CX18_AV_COMPOSITE7, CX18_AV_COMPOSITE8, - /* S-Video inputs consist of one luma input (In1-In4) ORed with one + /* S-Video inputs consist of one luma input (In1-In8) ORed with one chroma input (In5-In8) */ CX18_AV_SVIDEO_LUMA1 = 0x10, CX18_AV_SVIDEO_LUMA2 = 0x20, CX18_AV_SVIDEO_LUMA3 = 0x30, CX18_AV_SVIDEO_LUMA4 = 0x40, + CX18_AV_SVIDEO_LUMA5 = 0x50, + CX18_AV_SVIDEO_LUMA6 = 0x60, + CX18_AV_SVIDEO_LUMA7 = 0x70, + CX18_AV_SVIDEO_LUMA8 = 0x80, CX18_AV_SVIDEO_CHROMA4 = 0x400, CX18_AV_SVIDEO_CHROMA5 = 0x500, CX18_AV_SVIDEO_CHROMA6 = 0x600, diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 607efdc..1da6f13 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -433,7 +433,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp int chroma = vid_input & 0xf00; if ((vid_input & ~0xff0) || - luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA4 || + luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 || chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { v4l_err(client, "0x%04x is not a valid video input!\n", vid_input); diff --git a/include/media/cx25840.h b/include/media/cx25840.h index cd599ad..db431d5 100644 --- a/include/media/cx25840.h +++ b/include/media/cx25840.h @@ -32,12 +32,16 @@ enum cx25840_video_input { CX25840_COMPOSITE7, CX25840_COMPOSITE8, - /* S-Video inputs consist of one luma input (In1-In4) ORed with one + /* S-Video inputs consist of one luma input (In1-In8) ORed with one chroma input (In5-In8) */ CX25840_SVIDEO_LUMA1 = 0x10, CX25840_SVIDEO_LUMA2 = 0x20, CX25840_SVIDEO_LUMA3 = 0x30, CX25840_SVIDEO_LUMA4 = 0x40, + CX25840_SVIDEO_LUMA5 = 0x50, + CX25840_SVIDEO_LUMA6 = 0x60, + CX25840_SVIDEO_LUMA7 = 0x70, + CX25840_SVIDEO_LUMA8 = 0x80, CX25840_SVIDEO_CHROMA4 = 0x400, CX25840_SVIDEO_CHROMA5 = 0x500, CX25840_SVIDEO_CHROMA6 = 0x600, -- cgit v0.10.2 From d3d9b803eead0a536d28ffc31c5fadae976991cc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 8 Jun 2008 06:05:18 -0300 Subject: V4L/DVB (8008): cx18: remove duplicate audio and video input enums cx18-cards.h had a copy of the audio and video input enums from cx18-av-core.h, but with different prefixes. Removed that copy and used the ones from cx18-av-core.h. Thanks to Andy Walls for the report. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index baccd07..c35eb53 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -23,6 +23,7 @@ #include "cx18-driver.h" #include "cx18-cards.h" +#include "cx18-av-core.h" #include "cx18-i2c.h" #include @@ -54,22 +55,22 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { .hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | CX18_HW_CS5345 | CX18_HW_DVB, .video_inputs = { - { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 }, - { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 }, - { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 }, - { CX18_CARD_INPUT_SVIDEO2, 2, CX23418_SVIDEO2 }, - { CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 }, + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, + { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 }, + { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 }, + { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 }, }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, + CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, { CX18_CARD_INPUT_LINE_IN1, - CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_2 }, { CX18_CARD_INPUT_LINE_IN2, - CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_2 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, 0 }, .ddr = { /* ESMT M13S128324A-5B memory */ .chip_config = 0x003, @@ -94,22 +95,22 @@ static const struct cx18_card cx18_card_hvr1600_samsung = { .hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | CX18_HW_CS5345 | CX18_HW_DVB, .video_inputs = { - { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 }, - { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 }, - { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 }, - { CX18_CARD_INPUT_SVIDEO2, 2, CX23418_SVIDEO2 }, - { CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 }, + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, + { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 }, + { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 }, + { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 }, }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, + CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, { CX18_CARD_INPUT_LINE_IN1, - CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_2 }, { CX18_CARD_INPUT_LINE_IN2, - CX23418_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_2 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, 0 }, .ddr = { /* Samsung K4D263238G-VC33 memory */ .chip_config = 0x003, @@ -141,19 +142,19 @@ static const struct cx18_card cx18_card_h900 = { .hw_audio_ctrl = CX18_HW_CX23418, .hw_all = CX18_HW_TUNER, .video_inputs = { - { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE2 }, + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, { CX18_CARD_INPUT_SVIDEO1, 1, - CX23418_SVIDEO_LUMA3 | CX23418_SVIDEO_CHROMA4 }, - { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE1 }, + CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO8, 0 }, + CX18_AV_AUDIO8, 0 }, { CX18_CARD_INPUT_LINE_IN1, - CX23418_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, 0 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, 0 }, .tuners = { { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, }, @@ -188,18 +189,18 @@ static const struct cx18_card cx18_card_mpc718 = { .hw_audio_ctrl = CX18_HW_CX23418, .hw_all = CX18_HW_TUNER, .video_inputs = { - { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 }, - { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 }, - { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 }, + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, + { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 }, }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO8, 0 }, + CX18_AV_AUDIO8, 0 }, { CX18_CARD_INPUT_LINE_IN1, - CX23418_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, 0 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX23418_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, 0 }, .tuners = { /* XC3028 tuner */ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index bccb67f..3c2c0b9 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -36,36 +36,6 @@ #define CX18_CARD_INPUT_COMPOSITE2 5 #define CX18_CARD_INPUT_COMPOSITE3 6 -enum cx34180_video_input { - /* Composite video inputs In1-In8 */ - CX23418_COMPOSITE1 = 1, - CX23418_COMPOSITE2, - CX23418_COMPOSITE3, - CX23418_COMPOSITE4, - CX23418_COMPOSITE5, - CX23418_COMPOSITE6, - CX23418_COMPOSITE7, - CX23418_COMPOSITE8, - - /* S-Video inputs consist of one luma input (In1-In4) ORed with one - chroma input (In5-In8) */ - CX23418_SVIDEO_LUMA1 = 0x10, - CX23418_SVIDEO_LUMA2 = 0x20, - CX23418_SVIDEO_LUMA3 = 0x30, - CX23418_SVIDEO_LUMA4 = 0x40, - CX23418_SVIDEO_CHROMA4 = 0x400, - CX23418_SVIDEO_CHROMA5 = 0x500, - CX23418_SVIDEO_CHROMA6 = 0x600, - CX23418_SVIDEO_CHROMA7 = 0x700, - CX23418_SVIDEO_CHROMA8 = 0x800, - - /* S-Video aliases for common luma/chroma combinations */ - CX23418_SVIDEO1 = 0x510, - CX23418_SVIDEO2 = 0x620, - CX23418_SVIDEO3 = 0x730, - CX23418_SVIDEO4 = 0x840, -}; - /* audio inputs */ #define CX18_CARD_INPUT_AUD_TUNER 1 #define CX18_CARD_INPUT_LINE_IN1 2 @@ -75,16 +45,6 @@ enum cx34180_video_input { #define CX18_CARD_MAX_AUDIO_INPUTS 3 #define CX18_CARD_MAX_TUNERS 2 -enum cx23418_audio_input { - /* Audio inputs: serial or In4-In8 */ - CX23418_AUDIO_SERIAL, - CX23418_AUDIO4 = 4, - CX23418_AUDIO5, - CX23418_AUDIO6, - CX23418_AUDIO7, - CX23418_AUDIO8, -}; - /* V4L2 capability aliases */ #define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE) -- cgit v0.10.2 From 0367ca1bc7346d9ea89a4b4f1b9220489bda65a3 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Mon, 9 Jun 2008 14:58:04 -0300 Subject: V4L/DVB (8010): em28xx: Properly register extensions for already attached devices em28xx-video.c - Properly handle loading of the module when multiple devices are already connected (such as at bootup). Before we were only calling dvb_init() against the last device in the list, so while we were handling subsequent adds properly, if there were multiple devices present on driver load, everybody except the last device would not get initialized. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index fb163ec..285bc62 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1848,32 +1848,28 @@ static DEFINE_MUTEX(em28xx_extension_devlist_lock); int em28xx_register_extension(struct em28xx_ops *ops) { - struct em28xx *h, *dev = NULL; - - list_for_each_entry(h, &em28xx_devlist, devlist) - dev = h; + struct em28xx *dev = NULL; mutex_lock(&em28xx_extension_devlist_lock); list_add_tail(&ops->next, &em28xx_extension_devlist); - if (dev) - ops->init(dev); - + list_for_each_entry(dev, &em28xx_devlist, devlist) { + if (dev) + ops->init(dev); + } printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); mutex_unlock(&em28xx_extension_devlist_lock); - return 0; } EXPORT_SYMBOL(em28xx_register_extension); void em28xx_unregister_extension(struct em28xx_ops *ops) { - struct em28xx *h, *dev = NULL; - - list_for_each_entry(h, &em28xx_devlist, devlist) - dev = h; + struct em28xx *dev = NULL; - if (dev) - ops->fini(dev); + list_for_each_entry(dev, &em28xx_devlist, devlist) { + if (dev) + ops->fini(dev); + } mutex_lock(&em28xx_extension_devlist_lock); printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); -- cgit v0.10.2 From b38cc642000f0a262db367ffb95cd02ca2ead59b Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Mon, 9 Jun 2008 14:59:05 -0300 Subject: V4L/DVB (8011): em28xx: enable DVB for HVR-900 em28xx-cards.c - DVB support is supposed to be enabled for the first generation HVR-900. This device was confirmed with DVB by mkrufky when we did the original work in April, but I guess we forgot to set the flag. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 3e4f3c7..5cf7158 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -157,6 +157,7 @@ struct em28xx_board em28xx_boards[] = { .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, .mts_firmware = 1, + .has_dvb = 1, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, -- cgit v0.10.2 From f56ebe16b045861d26b81ef6683445c0144362b8 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 28 May 2008 21:55:06 -0300 Subject: V4L/DVB (8012): gl861: sleep a little to avoid I2C errors - add little sleep to avoid I2C errors arising on faster CPUs Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c index 0a8ac64..10cc436 100644 --- a/drivers/media/dvb/dvb-usb/gl861.c +++ b/drivers/media/dvb/dvb-usb/gl861.c @@ -47,6 +47,8 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, return -EINVAL; } + msleep(0); /* avoid I2C errors */ + return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type, value, index, rbuf, rlen, 2000); } -- cgit v0.10.2 From ea3a13b7a19f0d7d7344494047c9aa8bb32b6678 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 28 May 2008 22:04:12 -0300 Subject: V4L/DVB (8013): gl861: remove useless identify_state - remove useless identify_state - device is always warm Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c index 10cc436..9e53650 100644 --- a/drivers/media/dvb/dvb-usb/gl861.c +++ b/drivers/media/dvb/dvb-usb/gl861.c @@ -94,16 +94,6 @@ static struct i2c_algorithm gl861_i2c_algo = { }; /* Callbacks for DVB USB */ -static int gl861_identify_state(struct usb_device *udev, - struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, - int *cold) -{ - *cold = 0; - - return 0; -} - static struct zl10353_config gl861_zl10353_config = { .demod_address = 0x0f, .no_tuner = 1, @@ -174,7 +164,6 @@ static struct dvb_usb_device_properties gl861_properties = { .size_of_priv = 0, - .identify_state = gl861_identify_state, .num_adapters = 1, .adapter = {{ @@ -196,13 +185,15 @@ static struct dvb_usb_device_properties gl861_properties = { .num_device_descs = 2, .devices = { - { "MSI Mega Sky 55801 DVB-T USB2.0", - { &gl861_table[0], NULL }, - { NULL }, + { + .name = "MSI Mega Sky 55801 DVB-T USB2.0", + .cold_ids = { NULL }, + .warm_ids = { &gl861_table[0], NULL }, }, - { "A-LINK DTU DVB-T USB2.0", - { &gl861_table[1], NULL }, - { NULL }, + { + .name = "A-LINK DTU DVB-T USB2.0", + .cold_ids = { NULL }, + .warm_ids = { &gl861_table[1], NULL }, }, } }; -- cgit v0.10.2 From 1a78db826973b9fa658d72291ec2007889e2faf7 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 10 Jun 2008 11:41:58 -0300 Subject: V4L/DVB (8015): gl861: replace non critical msleep(0) with msleep(1) to be on the safe side - change msleep(0) to msleep(1) Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c index 9e53650..037f7ff 100644 --- a/drivers/media/dvb/dvb-usb/gl861.c +++ b/drivers/media/dvb/dvb-usb/gl861.c @@ -47,7 +47,7 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, return -EINVAL; } - msleep(0); /* avoid I2C errors */ + msleep(1); /* avoid I2C errors */ return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type, value, index, rbuf, rlen, 2000); -- cgit v0.10.2 From df619181631217e3166bb6c7538f981e0272617f Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Tue, 10 Jun 2008 12:34:35 -0300 Subject: V4L/DVB (8017): Ensure em28xx extensions only get run against devs that support them em28xx-audio.c em28xx-dvb.c - Em28xx extensions should ensure they are being only loaded against devices that support them. Deals with case where there are multiple em28xx devices, some of which have DVB (or ALSA) support and some do not. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index 92b2a6d..343dff0 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -415,6 +415,12 @@ static int em28xx_audio_init(struct em28xx *dev) static int devnr; int ret, err; + if (dev->has_audio_class) { + /* This device does not support the extension (in this case + the device is expecting the snd-usb-audio module */ + return 0; + } + printk(KERN_INFO "em28xx-audio.c: probing for em28x1 " "non standard usbaudio\n"); printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus " @@ -458,6 +464,12 @@ static int em28xx_audio_fini(struct em28xx *dev) if (dev == NULL) return 0; + if (dev->has_audio_class) { + /* This device does not support the extension (in this case + the device is expecting the snd-usb-audio module */ + return 0; + } + if (dev->adev) { snd_card_free(dev->adev->sndcard); kfree(dev->adev); diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 8cf4983..0b2333e 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -382,6 +382,11 @@ static int dvb_init(struct em28xx *dev) int result = 0; struct em28xx_dvb *dvb; + if (!dev->has_dvb) { + /* This device does not support the extension */ + return 0; + } + dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); if (dvb == NULL) { @@ -444,6 +449,11 @@ out_free: static int dvb_fini(struct em28xx *dev) { + if (!dev->has_dvb) { + /* This device does not support the extension */ + return 0; + } + if (dev->dvb) { unregister_dvb(dev->dvb); dev->dvb = NULL; -- cgit v0.10.2 From a8a1f8cc0cae07c209f13857adbdd4b87b36cdde Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Tue, 10 Jun 2008 12:35:42 -0300 Subject: V4L/DVB (8018): Add em2860 chip ID em28xx-cards.c em28xx-reg.h - Add em2860 chip ID (seen on Pointnix Intra-Oral Camera) http://www.pointnix.com/ENG/dental/product_02.asp Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 5cf7158..8cbda43 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -525,6 +525,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); if (rc > 0) { switch (rc) { + case CHIP_ID_EM2860: + em28xx_info("chip ID is em2860\n"); + break; case CHIP_ID_EM2883: em28xx_info("chip ID is em2882/em2883\n"); dev->wait_after_write = 0; diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index 9058bed..fac1ab2 100644 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ b/drivers/media/video/em28xx/em28xx-reg.h @@ -84,5 +84,6 @@ /* FIXME: Need to be populated with the other chip ID's */ enum em28xx_chip_id { + CHIP_ID_EM2860 = 34, CHIP_ID_EM2883 = 36, }; -- cgit v0.10.2 From a14fe9605bfdfe360b97acc9ef912779eb860507 Mon Sep 17 00:00:00 2001 From: Dmitri Belimov Date: Tue, 10 Jun 2008 14:19:31 -0300 Subject: V4L/DVB (8020): Fix callbacks functions of saa7134_empress If I try v4l2-ctl --all -d /dev/video1 or v4l2-ctl --streamon -d /dev/video1 modules crashed: *pde = 00000000 Modules linked in: ac battery loop saa7134_empress(F) saa6752hs(F) tuner_simple(F) tuner_types(F) tea5767(F) tda9887(F) tda8290(F) tea5761(F) tuner(F) snd_cmipci snd_pcm snd_page_alloc snd_opl3_lib saa7134(F) snd_mpu401 parport_pc parport snd_timer snd_hwdep snd_mpu401_uart floppy rtc psmouse videodev(F) v4l1_compat(F) compat_ioctl32(F) v4l2_common(F) videobuf_dma_sg(F) videobuf_core(F) snd_rawmidi snd_seq_device via_ircc pcspkr snd ir_kbd_i2c(F) irda soundcore ir_common(F) crc_ccitt tveeprom(F) i2c_viapro i2c_core button via_agp agpgart evdev ext3 jbd mbcache ide_cd_mod cdrom ide_disk 8139cp via82cxxx ide_core 8139too mii ehci_hcd uhci_hcd usbcore thermal processor fan EIP is at __mutex_lock_slowpath+0x29/0x7b DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 Process v4l2-ctl (pid: 2742, ti=ced7e000 task=cf325260 task.ti=ced7e000) d08e5411 00000000 ced7fed4 00000000 d0975acb 40045612 cfa86ee0 ffffffcd cf2b7000 ced7febc c03858d6 00000019 00000292 d089e4ec cf37b2a0 d089e4a0 Call Trace: [] mutex_lock+0xa/0xb [] videobuf_streamon+0xf/0x9a [videobuf_core] [] __video_do_ioctl+0x136a/0x2d68 [videodev] [] task_end_request+0x40/0x51 [ide_core] [] ide_intr+0x187/0x192 [ide_core] [] mntput_no_expire+0x11/0x64 [] path_walk+0x90/0x98 [] video_ioctl2+0x173/0x239 [videodev] [] filemap_fault+0x202/0x370 [] __do_fault+0x2c3/0x2fe [] handle_mm_fault+0x22a/0x49f [] vfs_ioctl+0x47/0x5d [] do_vfs_ioctl+0x245/0x258 [] sys_ioctl+0x41/0x5b [] sysenter_past_esp+0x5f/0x85 ======================= After this fix all of that commands works without problem: v4l2-ctl --all -d /dev/video1 Driver Info: Driver name : saa7134 Card type : Beholder BeholdTV M6 Extra Bus info : PCI:0000:00:0d.0 Driver version: 526 Capabilities : 0x05000001 Video Capture Read/Write Streaming Format Video Capture: Width/Height : 720/576 Pixel Format : MPEG Field : Any Bytes per Line: 0 Size Image : 58656 Colorspace : Unknown (00000000) Video input : 0 (CCIR656) Video Standard = 0x000000ff PAL-B/B1/G/H/I/D/D1/K P.S. data from /dev/video1 is not correct :(( . Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index 81431ee..94b2585 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -218,8 +218,7 @@ static int empress_enum_fmt_cap(struct file *file, void *priv, static int empress_g_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; saa7134_i2c_call_clients(dev, VIDIOC_G_FMT, f); @@ -232,8 +231,7 @@ static int empress_g_fmt_cap(struct file *file, void *priv, static int empress_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; saa7134_i2c_call_clients(dev, VIDIOC_S_FMT, f); @@ -247,8 +245,7 @@ static int empress_s_fmt_cap(struct file *file, void *priv, static int empress_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_reqbufs(&dev->empress_tsq, p); } @@ -256,24 +253,21 @@ static int empress_reqbufs(struct file *file, void *priv, static int empress_querybuf(struct file *file, void *priv, struct v4l2_buffer *b) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_querybuf(&dev->empress_tsq, b); } static int empress_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_qbuf(&dev->empress_tsq, b); } static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_dqbuf(&dev->empress_tsq, b, file->f_flags & O_NONBLOCK); @@ -282,8 +276,7 @@ static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) static int empress_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_streamon(&dev->empress_tsq); } @@ -291,8 +284,7 @@ static int empress_streamon(struct file *file, void *priv, static int empress_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; return videobuf_streamoff(&dev->empress_tsq); } @@ -300,8 +292,7 @@ static int empress_streamoff(struct file *file, void *priv, static int empress_s_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *ctrls) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; /* count == 0 is abused in saa6752hs.c, so that special case is handled here explicitly. */ @@ -320,8 +311,7 @@ static int empress_s_ext_ctrls(struct file *file, void *priv, static int empress_g_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *ctrls) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; + struct saa7134_dev *dev = file->private_data; if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) return -EINVAL; -- cgit v0.10.2 From 913f5fc209247b607b1994a710315966f4f9d358 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Tue, 10 Jun 2008 15:18:16 -0300 Subject: V4L/DVB (8022): saa7134: fix race between opening and closing the device decrementing dev->empress_users should be done as last action of ts_release, because it sleeps and write access to dev->empress_started is not protected in any way (additionally closing thread could mute audio after opening thread unmuted it) Signed-off-by: Marcin Slusarz Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index 94b2585..1c8cd0e 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -112,7 +112,6 @@ static int ts_release(struct inode *inode, struct file *file) videobuf_stop(&dev->empress_tsq); videobuf_mmap_free(&dev->empress_tsq); - dev->empress_users--; /* stop the encoder */ ts_reset_encoder(dev); @@ -121,6 +120,8 @@ static int ts_release(struct inode *inode, struct file *file) saa_writeb(SAA7134_AUDIO_MUTE_CTRL, saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6)); + dev->empress_users--; + return 0; } -- cgit v0.10.2 From 83ee87a31dc43a5fd6dee3562c146033c3a4cb39 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 14 Jun 2008 09:41:18 -0300 Subject: V4L/DVB (8026): Avoids an OOPS if dev struct can't be successfully recovered On some alsa versions, it seems that snd_pcm_substream_chip(substream) is returning a NULL pointer. This causes an OOPS, as reported by: https://bugs.launchpad.net/ubuntu/+source/linux-ubuntu-modules-2.6.24/+bug/212271 https://bugs.launchpad.net/ubuntu/+source/alsa-driver/+bug/212960 This patch avoids the OOPS by not letting and open() succeed. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index e976fc6..80c8883 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -332,6 +332,12 @@ static int snd_cx88_pcm_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; int err; + if (!chip) { + printk(KERN_ERR "BUG: cx88 can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) goto _error; diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index 343dff0..3c00610 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -268,6 +268,12 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) dprintk("opening device and trying to acquire exclusive lock\n"); + if (!dev) { + printk(KERN_ERR "BUG: em28xx can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + /* Sets volume, mute, etc */ dev->mute = 0; diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index ba30824..f118de6 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -613,9 +613,15 @@ static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream) struct snd_pcm_runtime *runtime = substream->runtime; snd_card_saa7134_pcm_t *pcm; snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); - struct saa7134_dev *dev = saa7134->dev; + struct saa7134_dev *dev; int amux, err; + if (!saa7134) { + printk(KERN_ERR "BUG: saa7134 can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + dev = saa7134->dev; mutex_lock(&dev->dmasound.lock); dev->dmasound.read_count = 0; -- cgit v0.10.2 From b25fed115a8bd8d39d14ce0d03e808272a401d67 Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Fri, 13 Jun 2008 19:28:17 -0300 Subject: V4L/DVB (8027): saa7134: Avermedia A700: only s-video and composite input are working Describe exactly that only s-video and composite input are working on Avermedia A700 Signed-off-by: Matthias Schwarzott Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index b111903..21a7612 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5615,7 +5615,8 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100); printk("%s: %s: hybrid analog/dvb card\n" - "%s: Sorry, only the analog inputs are supported for now.\n", + "%s: Sorry, only analog s-video and composite input " + "are supported for now.\n", dev->name, card(dev).name, dev->name); break; } -- cgit v0.10.2 From bc36ec746409e4e4719b94a86dc0d8cbeb6f439f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 14 Jun 2008 10:44:04 -0300 Subject: V4L/DVB (8028): Improve error messages for tda1004x attach A fresh copy of v.29 firmware, using get_firmware, is leading to an invalid firmware: DVB: registering new adapter (saa7133[0]) DVB: registering frontend 0 (Philips TDA10046H DVB-T)... tda1004x: setting up plls for 48MHz sampling clock tda1004x: found firmware revision ff -- invalid tda1004x: trying to boot from eeprom tda1004x: found firmware revision ff -- invalid tda1004x: waiting for firmware upload... tda1004x: Error during firmware upload tda1004x: found firmware revision ff -- invalid tda1004x: firmware upload failed Sometimes, loading/unloading this firmware makes tda1004x to return an invalid ID. However, there were no printk messages to help to identify what were the cause for the error. With this patch, it will now print: Invalid tda1004x ID = 0xff. Can't proceed saa7133[0]/dvb: failed to attach tda10046 saa7133[0]/dvb: frontend initialization failed Tested with LifeView FlyDVB-T Hybrid Cardbus/MSI TV @nywhere A/D NB Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c index 4997384..3993d1c 100644 --- a/drivers/media/dvb/frontends/tda1004x.c +++ b/drivers/media/dvb/frontends/tda1004x.c @@ -1248,11 +1248,14 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, struct i2c_adapter* i2c) { struct tda1004x_state *state; + u8 id; /* allocate memory for the internal state */ state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); - if (!state) + if (!state) { + printk(KERN_ERR "Can't alocate memory for tda10045 state\n"); return NULL; + } /* setup the state */ state->config = config; @@ -1260,7 +1263,9 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, state->demod_type = TDA1004X_DEMOD_TDA10045; /* check if the demod is there */ - if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x25) { + id = tda1004x_read_byte(state, TDA1004X_CHIPID); + if (id != 0x25) { + printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id); kfree(state); return NULL; } @@ -1307,11 +1312,14 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, struct i2c_adapter* i2c) { struct tda1004x_state *state; + u8 id; /* allocate memory for the internal state */ state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); - if (!state) + if (!state) { + printk(KERN_ERR "Can't alocate memory for tda10046 state\n"); return NULL; + } /* setup the state */ state->config = config; @@ -1319,7 +1327,9 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, state->demod_type = TDA1004X_DEMOD_TDA10046; /* check if the demod is there */ - if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x46) { + id = tda1004x_read_byte(state, TDA1004X_CHIPID); + if (id != 0x46) { + printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id); kfree(state); return NULL; } diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 726f01c..653107c 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -1091,7 +1091,8 @@ static int dvb_init(struct saa7134_dev *dev) ads_tech_duo_config.tuner_address); goto dettach_frontend; } - } + } else + wprintk("failed to attach tda10046\n"); break; case SAA7134_BOARD_TEVION_DVBT_220RF: if (configure_tda827x_fe(dev, &tevion_dvbt220rf_config, -- cgit v0.10.2 From 0e7830b50b20fcc25f21f79b7734102284d7c8f9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 14 Jun 2008 11:27:34 -0300 Subject: V4L/DVB (8029): Improve error message at tda1004x_attach When an error occurs at firmware loading, sometimes, tda1004x stops answering. Instead of reporting such error, attach code were assuming that the device were answering an invalid ID (0xff). This can be seen when enabling debug options: tda1004x: tda1004x_read_byte: reg=0x0 tda1004x: tda1004x_read_byte: error reg=0x0, ret=-5 Now, instead of reporting an invalid ID, it will report the correct error: tda10046: chip is not answering. Giving up. saa7133[0]/dvb: failed to attach tda10046 saa7133[0]/dvb: frontend initialization failed A possible improvement would be trying to reset the device. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c index 3993d1c..a0d6386 100644 --- a/drivers/media/dvb/frontends/tda1004x.c +++ b/drivers/media/dvb/frontends/tda1004x.c @@ -1248,7 +1248,7 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, struct i2c_adapter* i2c) { struct tda1004x_state *state; - u8 id; + int id; /* allocate memory for the internal state */ state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); @@ -1264,6 +1264,12 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, /* check if the demod is there */ id = tda1004x_read_byte(state, TDA1004X_CHIPID); + if (id < 0) { + printk(KERN_ERR "tda10045: chip is not answering. Giving up.\n"); + kfree(state); + return NULL; + } + if (id != 0x25) { printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id); kfree(state); @@ -1312,7 +1318,7 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, struct i2c_adapter* i2c) { struct tda1004x_state *state; - u8 id; + int id; /* allocate memory for the internal state */ state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); @@ -1328,6 +1334,11 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, /* check if the demod is there */ id = tda1004x_read_byte(state, TDA1004X_CHIPID); + if (id < 0) { + printk(KERN_ERR "tda10046: chip is not answering. Giving up.\n"); + kfree(state); + return NULL; + } if (id != 0x46) { printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id); kfree(state); -- cgit v0.10.2 From 44e645c20304bbe0a72cb994d9baf4b5727d7cec Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 8 Jun 2008 20:10:29 -0300 Subject: V4L/DVB (8034): tda18271: fix IF notch frequency handling The IF notch bit gets unset when we update the Main Post Div register value, before we have a chance to write the desired IF notch setting to the tuner. Move the IF notch configuration to after we update MPD. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index 89c01fb..98acd58 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -45,6 +45,21 @@ static inline int charge_pump_source(struct dvb_frontend *fe, int force) TDA18271_MAIN_PLL, force); } +static inline void tda18271_set_if_notch(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + + switch (priv->mode) { + case TDA18271_ANALOG: + regs[R_MPD] &= ~0x80; /* IF notch = 0 */ + break; + case TDA18271_DIGITAL: + regs[R_MPD] |= 0x80; /* IF notch = 1 */ + break; + } +} + static int tda18271_channel_configuration(struct dvb_frontend *fe, struct tda18271_std_map_item *map, u32 freq, u32 bw) @@ -66,19 +81,10 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, /* set cal mode to normal */ regs[R_EP4] &= ~0x03; - /* update IF output level & IF notch frequency */ + /* update IF output level */ regs[R_EP4] &= ~0x1c; /* clear if level bits */ regs[R_EP4] |= (map->if_lvl << 2); - switch (priv->mode) { - case TDA18271_ANALOG: - regs[R_MPD] &= ~0x80; /* IF notch = 0 */ - break; - case TDA18271_DIGITAL: - regs[R_MPD] |= 0x80; /* IF notch = 1 */ - break; - } - /* update FM_RFn */ regs[R_EP4] &= ~0x80; regs[R_EP4] |= map->fm_rfn << 7; @@ -135,6 +141,7 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, switch (priv->role) { case TDA18271_MASTER: tda18271_calc_main_pll(fe, N); + tda18271_set_if_notch(fe); tda18271_write_regs(fe, R_MPD, 4); break; case TDA18271_SLAVE: @@ -142,6 +149,7 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, tda18271_write_regs(fe, R_CPD, 4); regs[R_MPD] = regs[R_CPD] & 0x7f; + tda18271_set_if_notch(fe); tda18271_write_regs(fe, R_MPD, 1); break; } @@ -507,7 +515,7 @@ static int tda18271_powerscan_init(struct dvb_frontend *fe) /* set cal mode to normal */ regs[R_EP4] &= ~0x03; - /* update IF output level & IF notch frequency */ + /* update IF output level */ regs[R_EP4] &= ~0x1c; /* clear if level bits */ ret = tda18271_write_regs(fe, R_EP3, 2); -- cgit v0.10.2 From 119a7c7e34b5e5fa65051960ca05000a19dbce85 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 8 Jun 2008 22:12:37 -0300 Subject: V4L/DVB (8035): tda18271: dont touch EB14 if rf_cal lookup is out of range The TDA18271HD/C1 rf_cal map lookup is expected to go out of range outside of the frequency window 41 MHz - 61.1 MHz. In these cases, the internal RF tracking filters calibration mechanism is used. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c index f1894fe..6fb5b45 100644 --- a/drivers/media/common/tuners/tda18271-common.c +++ b/drivers/media/common/tuners/tda18271-common.c @@ -649,9 +649,17 @@ int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq) u8 val; int ret = tda18271_lookup_map(fe, RF_CAL, freq, &val); + /* The TDA18271HD/C1 rf_cal map lookup is expected to go out of range + * for frequencies above 61.1 MHz. In these cases, the internal RF + * tracking filters calibration mechanism is used. + * + * There is no need to warn the user about this. + */ + if (ret < 0) + goto fail; regs[R_EB14] = val; - +fail: return ret; } -- cgit v0.10.2 From 51858d13612a06494beb08616f8a93bdb2688e4c Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 9 Jun 2008 02:03:31 -0300 Subject: V4L/DVB (8036): tda18271: toggle rf agc speed mode on TDA18271HD/C2 only Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index 98acd58..1ebf95d 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -75,8 +75,10 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, regs[R_EP3] &= ~0x1f; /* clear std bits */ regs[R_EP3] |= (map->agc_mode << 3) | map->std; - /* set rfagc to high speed mode */ - regs[R_EP3] &= ~0x04; + if (priv->id == TDA18271HDC2) { + /* set rfagc to high speed mode */ + regs[R_EP3] &= ~0x04; + } /* set cal mode to normal */ regs[R_EP4] &= ~0x03; @@ -168,12 +170,14 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, msleep(20); - /* set rfagc to normal speed mode */ - if (map->fm_rfn) - regs[R_EP3] &= ~0x04; - else - regs[R_EP3] |= 0x04; - ret = tda18271_write_regs(fe, R_EP3, 1); + if (priv->id == TDA18271HDC2) { + /* set rfagc to normal speed mode */ + if (map->fm_rfn) + regs[R_EP3] &= ~0x04; + else + regs[R_EP3] |= 0x04; + ret = tda18271_write_regs(fe, R_EP3, 1); + } fail: return ret; } -- cgit v0.10.2 From 7ae1ac4c1db5a3647604acb00d736f17af6999b7 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 11 Jun 2008 13:52:49 -0300 Subject: V4L/DVB (8037): tda18271: ensure that the thermometer is off during channel configuration Having the thermometer on during channel configuration could cause tuning instability. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index 1ebf95d..93063c6 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -103,6 +103,9 @@ static int tda18271_channel_configuration(struct dvb_frontend *fe, /* disable Power Level Indicator */ regs[R_EP1] |= 0x40; + /* make sure thermometer is off */ + regs[R_TM] &= ~0x10; + /* frequency dependent parameters */ tda18271_calc_ir_measure(fe, &freq); -- cgit v0.10.2 From 02da465945ae0a277aadf2bf37965a9e6c28f8c3 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 13 Jun 2008 09:03:45 -0300 Subject: V4L/DVB (8039): pxa-camera: fix platform_get_irq() error handling. platform_get_irq() returns a negative value on error, not 0. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 7cc8e9b..5ec5bb9 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -1019,12 +1019,12 @@ static int pxa_camera_probe(struct platform_device *pdev) struct pxa_camera_dev *pcdev; struct resource *res; void __iomem *base; - unsigned int irq; + int irq; int err = 0; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (!res || !irq) { + if (!res || irq < 0) { err = -ENODEV; goto exit; } -- cgit v0.10.2 From 3cfdc7f25b3116d57e91427caada74dd69bfbc06 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 13 Jun 2008 09:11:01 -0300 Subject: V4L/DVB (8040): soc-camera: remove soc_camera_host_class class Devices can either be class devices or bus devices, not both at the same time. Soc-camera host devices usually have a platform device as their parent. Trying to also register them with a class crashes the kernel, when linked statically. Interestingly, it works when built as a module. Thanks to Paulius Zaleckas for reporting. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index a1b9244..d015bfe 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -763,15 +763,6 @@ static struct device_driver ic_drv = { .owner = THIS_MODULE, }; -/* - * Image capture host - this is a host device, not a bus device, so, - * no bus reference, no probing. - */ -static struct class soc_camera_host_class = { - .owner = THIS_MODULE, - .name = "camera_host", -}; - static void dummy_release(struct device *dev) { } @@ -801,7 +792,6 @@ int soc_camera_host_register(struct soc_camera_host *ici) /* Number might be equal to the platform device ID */ sprintf(ici->dev.bus_id, "camera_host%d", ici->nr); - ici->dev.class = &soc_camera_host_class; mutex_lock(&list_lock); list_for_each_entry(ix, &hosts, list) { @@ -1003,14 +993,9 @@ static int __init soc_camera_init(void) ret = driver_register(&ic_drv); if (ret) goto edrvr; - ret = class_register(&soc_camera_host_class); - if (ret) - goto eclr; return 0; -eclr: - driver_unregister(&ic_drv); edrvr: bus_unregister(&soc_camera_bus_type); return ret; @@ -1018,7 +1003,6 @@ edrvr: static void __exit soc_camera_exit(void) { - class_unregister(&soc_camera_host_class); driver_unregister(&ic_drv); bus_unregister(&soc_camera_bus_type); } -- cgit v0.10.2 From 3b9408870757bd9e07fd03ac6318258f22b8dfa3 Mon Sep 17 00:00:00 2001 From: Austin Lund Date: Fri, 13 Jun 2008 11:02:43 -0300 Subject: V4L/DVB (8042): DVB-USB UMT-010 channel scan oops In the umt-010 driver the struct umt_properties sets the number of URBs for transfer to 20. But in dvb-usb.h MAX_NO_URBS_FOR_DATA_STREAM is set to 10. Not surprisingly this causes an oops for all devices which use the umt-010 chipset when they are inserted. fix on Kaffeine channel scan for Initialize stream count using MAX_NO_URBS_FOR_DATA_STREAM. Signed-off-by: Tim Gardner Signed-off-by: maximilian attems Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c index 9e7653b..118aab1 100644 --- a/drivers/media/dvb/dvb-usb/umt-010.c +++ b/drivers/media/dvb/dvb-usb/umt-010.c @@ -107,7 +107,7 @@ static struct dvb_usb_device_properties umt_properties = { /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 20, + .count = MAX_NO_URBS_FOR_DATA_STREAM, .endpoint = 0x06, .u = { .bulk = { -- cgit v0.10.2 From 104fe9a2d2a56f25fb95800a7ab0f7600dd6879c Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Fri, 13 Jun 2008 03:29:43 -0300 Subject: V4L/DVB (8043): au0828: add support for additional USB device id's Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/CARDLIST.au0828 b/Documentation/video4linux/CARDLIST.au0828 index aaae360..86d1c8e 100644 --- a/Documentation/video4linux/CARDLIST.au0828 +++ b/Documentation/video4linux/CARDLIST.au0828 @@ -1,4 +1,4 @@ 0 -> Unknown board (au0828) - 1 -> Hauppauge HVR950Q (au0828) [2040:7200] + 1 -> Hauppauge HVR950Q (au0828) [2040:7200,2040:7210,2040:7217,2040:721b,2040:721f,2040:7280,0fd9:0008] 2 -> Hauppauge HVR850 (au0828) [2040:7240] 3 -> DViCO FusionHDTV USB (au0828) [0fe9:d620] diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c index a2a6983..898e123 100644 --- a/drivers/media/video/au0828/au0828-cards.c +++ b/drivers/media/video/au0828/au0828-cards.c @@ -77,8 +77,14 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) /* Make sure we support the board model */ switch (tv.model) { + case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */ case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and basic analog video */ + case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ + case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ + case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ + case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and basic analog video */ case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and basic analog video */ + case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ break; default: printk(KERN_WARNING "%s: warning: " @@ -175,6 +181,18 @@ struct usb_device_id au0828_usb_id_table [] = { .driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 }, { USB_DEVICE(0x0fe9, 0xd620), .driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 }, + { USB_DEVICE(0x2040, 0x7210), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7217), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x721b), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x721f), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7280), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x0fd9, 0x0008), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { }, }; -- cgit v0.10.2 From 74d50724a02d7cdc7f7887411518ec43d0251b97 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Fri, 13 Jun 2008 20:33:23 -0300 Subject: V4L/DVB (8044): au8522: tuning optimizations If the current modulation and frequency is already set to the desired parameters, then don't re-tune. Don't store current frequency until after we've tuned successfully. Force a re-tune after resume from standby. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/au8522.c b/drivers/media/dvb/frontends/au8522.c index 084a280..978a287 100644 --- a/drivers/media/dvb/frontends/au8522.c +++ b/drivers/media/dvb/frontends/au8522.c @@ -463,10 +463,13 @@ static int au8522_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) { struct au8522_state *state = fe->demodulator_priv; + int ret = -EINVAL; dprintk("%s(frequency=%d)\n", __func__, p->frequency); - state->current_frequency = p->frequency; + if ((state->current_frequency == p->frequency) && + (state->current_modulation == p->u.vsb.modulation)) + return 0; au8522_enable_modulation(fe, p->u.vsb.modulation); @@ -476,11 +479,16 @@ static int au8522_set_frontend(struct dvb_frontend *fe, if (fe->ops.tuner_ops.set_params) { if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - fe->ops.tuner_ops.set_params(fe, p); + ret = fe->ops.tuner_ops.set_params(fe, p); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); } + if (ret < 0) + return ret; + + state->current_frequency = p->frequency; + return 0; } @@ -498,6 +506,16 @@ static int au8522_init(struct dvb_frontend *fe) return 0; } +static int au8522_sleep(struct dvb_frontend *fe) +{ + struct au8522_state *state = fe->demodulator_priv; + dprintk("%s()\n", __func__); + + state->current_frequency = 0; + + return 0; +} + static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct au8522_state *state = fe->demodulator_priv; @@ -672,6 +690,7 @@ static struct dvb_frontend_ops au8522_ops = { }, .init = au8522_init, + .sleep = au8522_sleep, .i2c_gate_ctrl = au8522_i2c_gate_ctrl, .set_frontend = au8522_set_frontend, .get_frontend = au8522_get_frontend, -- cgit v0.10.2 From 6e501a3f4a7259b1c04aa6cbdfe64376afc9f59c Mon Sep 17 00:00:00 2001 From: Tim Farrington Date: Sun, 15 Jun 2008 13:33:42 -0300 Subject: V4L/DVB (8048): saa7134: Fix entries for Avermedia A16d and Avermedia E506 Also, adds IR table for Avermedia A16d Signed-off-by: Tim Farrington Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index a348581..8fa91f8 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -2201,3 +2201,41 @@ IR_KEYTAB_TYPE ir_codes_powercolor_real_angel[IR_KEYTAB_SIZE] = { [0x25] = KEY_POWER, /* power */ }; EXPORT_SYMBOL_GPL(ir_codes_powercolor_real_angel); + +IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE] = { + [0x20] = KEY_LIST, + [0x00] = KEY_POWER, + [0x28] = KEY_1, + [0x18] = KEY_2, + [0x38] = KEY_3, + [0x24] = KEY_4, + [0x14] = KEY_5, + [0x34] = KEY_6, + [0x2c] = KEY_7, + [0x1c] = KEY_8, + [0x3c] = KEY_9, + [0x12] = KEY_SUBTITLE, + [0x22] = KEY_0, + [0x32] = KEY_REWIND, + [0x3a] = KEY_SHUFFLE, + [0x02] = KEY_PRINT, + [0x11] = KEY_CHANNELDOWN, + [0x31] = KEY_CHANNELUP, + [0x0c] = KEY_ZOOM, + [0x1e] = KEY_VOLUMEDOWN, + [0x3e] = KEY_VOLUMEUP, + [0x0a] = KEY_MUTE, + [0x04] = KEY_AUDIO, + [0x26] = KEY_RECORD, + [0x06] = KEY_PLAY, + [0x36] = KEY_STOP, + [0x16] = KEY_PAUSE, + [0x2e] = KEY_REWIND, + [0x0e] = KEY_FASTFORWARD, + [0x30] = KEY_TEXT, + [0x21] = KEY_GREEN, + [0x01] = KEY_BLUE, + [0x08] = KEY_EPG, + [0x2a] = KEY_MENU, +}; +EXPORT_SYMBOL_GPL(ir_codes_avermedia_a16d); diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 21a7612..2618cfa 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -4114,11 +4114,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - /* - TODO: .mpeg = SAA7134_MPEG_DVB, - */ - .inputs = {{ .name = name_tv, .vmux = 1, @@ -4157,7 +4153,7 @@ struct saa7134_board saa7134_boards[] = { } }, .radio = { .name = name_radio, - .amux = LINE1, + .amux = TV, }, }, [SAA7134_BOARD_AVERMEDIA_M115] = { @@ -4167,6 +4163,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, .inputs = {{ .name = name_tv, .vmux = 1, @@ -5351,22 +5348,21 @@ static int saa7134_xc2028_callback(struct saa7134_dev *dev, { switch (command) { case XC2028_TUNER_RESET: - saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0x06e20000); - saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0x06a20000); - mdelay(250); - saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0); - saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0); - mdelay(250); - saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0x06e20000); - saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0x06a20000); - mdelay(250); - saa_andorl(SAA7133_ANALOG_IO_SELECT >> 2, 0x02, 0x02); - saa_andorl(SAA7134_ANALOG_IN_CTRL1 >> 2, 0x81, 0x81); - saa_andorl(SAA7134_AUDIO_CLOCK0 >> 2, 0x03187de7, 0x03187de7); - saa_andorl(SAA7134_AUDIO_PLL_CTRL >> 2, 0x03, 0x03); - saa_andorl(SAA7134_AUDIO_CLOCKS_PER_FIELD0 >> 2, - 0x0001e000, 0x0001e000); - return 0; + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00000000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000); + switch (dev->board) { + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + saa7134_set_gpio(dev, 23, 0); + msleep(10); + saa7134_set_gpio(dev, 23, 1); + break; + case SAA7134_BOARD_AVERMEDIA_A16D: + saa7134_set_gpio(dev, 21, 0); + msleep(10); + saa7134_set_gpio(dev, 21, 1); + break; + } + return 0; } return -EINVAL; } @@ -5553,9 +5549,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08000000, 0x00000000); break; case SAA7134_BOARD_AVERMEDIA_CARDBUS: - case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: case SAA7134_BOARD_AVERMEDIA_M115: - case SAA7134_BOARD_AVERMEDIA_A16D: /* power-down tuner chip */ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0xffffffff, 0); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0); @@ -5565,6 +5559,18 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0xffffffff); msleep(10); break; + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + saa7134_set_gpio(dev, 23, 0); + msleep(10); + saa7134_set_gpio(dev, 23, 1); + break; + case SAA7134_BOARD_AVERMEDIA_A16D: + saa7134_set_gpio(dev, 21, 0); + msleep(10); + saa7134_set_gpio(dev, 21, 1); + msleep(1); + dev->has_remote = SAA7134_REMOTE_GPIO; + break; case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: /* power-down tuner chip */ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004); @@ -5676,6 +5682,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev) switch (dev->board) { case SAA7134_BOARD_AVERMEDIA_A16D: + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: ctl.demod = XC3028_FE_ZARLINK456; break; default: diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 653107c..341b101 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -153,12 +153,12 @@ static int mt352_aver777_init(struct dvb_frontend* fe) return 0; } -static int mt352_aver_a16d_init(struct dvb_frontend *fe) +static int mt352_avermedia_xc3028_init(struct dvb_frontend *fe) { - static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; - static u8 reset [] = { RESET, 0x80 }; - static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; - static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 }; + static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; + static u8 reset [] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg [] = { AGC_TARGET, 0xe }; static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 }; mt352_write(fe, clock_config, sizeof(clock_config)); @@ -167,12 +167,9 @@ static int mt352_aver_a16d_init(struct dvb_frontend *fe) mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); mt352_write(fe, agc_cfg, sizeof(agc_cfg)); mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); - return 0; } - - static int mt352_pinnacle_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { @@ -215,14 +212,10 @@ static struct mt352_config avermedia_777 = { .demod_init = mt352_aver777_init, }; -static struct mt352_config avermedia_16d = { - .demod_address = 0xf, - .demod_init = mt352_aver_a16d_init, -}; - -static struct mt352_config avermedia_e506r_mt352_dev = { +static struct mt352_config avermedia_xc3028_mt352_dev = { .demod_address = (0x1e >> 1), .no_tuner = 1, + .demod_init = mt352_avermedia_xc3028_init, }; /* ================================================================== @@ -975,9 +968,10 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_AVERMEDIA_A16D: - dprintk("avertv A16D dvb setup\n"); - dev->dvb.frontend = dvb_attach(mt352_attach, &avermedia_16d, - &dev->i2c_adap); + dprintk("AverMedia A16D dvb setup\n"); + dev->dvb.frontend = dvb_attach(mt352_attach, + &avermedia_xc3028_mt352_dev, + &dev->i2c_adap); attach_xc3028 = 1; break; case SAA7134_BOARD_MD7134: @@ -1261,11 +1255,14 @@ static int dvb_init(struct saa7134_dev *dev) goto dettach_frontend; break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + dprintk("AverMedia E506R dvb setup\n"); + saa7134_set_gpio(dev, 25, 0); + msleep(10); + saa7134_set_gpio(dev, 25, 1); dev->dvb.frontend = dvb_attach(mt352_attach, - &avermedia_e506r_mt352_dev, - &dev->i2c_adap); + &avermedia_xc3028_mt352_dev, + &dev->i2c_adap); attach_xc3028 = 1; - break; case SAA7134_BOARD_MD7134_BRIDGE_2: dev->dvb.frontend = dvb_attach(tda10086_attach, &sd1878_4m, &dev->i2c_adap); diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 919632b..76e6501 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -323,6 +323,15 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPMODE1, 0x1); saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); break; + case SAA7134_BOARD_AVERMEDIA_A16D: + ir_codes = ir_codes_avermedia_a16d; + mask_keycode = 0x02F200; + mask_keydown = 0x000400; + polling = 50; /* ms */ + /* Without this we won't receive key up events */ + saa_setb(SAA7134_GPIO_GPMODE1, 0x1); + saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); + break; case SAA7134_BOARD_KWORLD_TERMINATOR: ir_codes = ir_codes_pixelview; mask_keycode = 0x00001f; diff --git a/include/media/ir-common.h b/include/media/ir-common.h index bfee8be..b8e8aa9 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -146,6 +146,7 @@ extern IR_KEYTAB_TYPE ir_codes_behold_columbus[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_pinnacle_pctv_hd[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_genius_tvgo_a11mce[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_powercolor_real_angel[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE]; #endif -- cgit v0.10.2 From beb31e636501f39e853ac5d0ffd3fbb910d19d5c Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 17 Jun 2008 17:37:51 -0300 Subject: V4L/DVB (8061): cx18: only select tuner / frontend modules if !DVB_FE_CUSTOMISE The automatic Kconfig selection for tuners and frontends should be conditional, based on !DVB_FE_CUSTOMISE. This patch corrects the selection for VIDEO_CX18 on MEDIA_TUNER_MXL5005S and DVB_S5H1409 Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig index 5f94269..9aefdc5 100644 --- a/drivers/media/video/cx18/Kconfig +++ b/drivers/media/video/cx18/Kconfig @@ -10,8 +10,8 @@ config VIDEO_CX18 select VIDEO_TVEEPROM select VIDEO_CX2341X select VIDEO_CS5345 - select DVB_S5H1409 - select MEDIA_TUNER_MXL5005S + select DVB_S5H1409 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE ---help--- This is a video4linux driver for Conexant cx23418 based PCI combo video recorder devices. -- cgit v0.10.2 From 527629fb7c35ad93389ab132823d19139dd88e70 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 14 Jun 2008 03:58:08 -0300 Subject: V4L/DVB (8063): cx18: Fix unintended auto configurations in cx18-av-core Change the cx18-av-core code so that accesses to cx23418 av core that cause auto-configuration will be adjusted to emulate the auto-configuration behavior of the cx25843. This fixes the VBI displayed as video at the top of the frame for NTSC and probably other things. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 8f2959a..faca43e 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -69,6 +69,58 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, or_value); } +int cx18_av_write_no_acfg(struct cx18 *cx, u16 addr, u8 value, int no_acfg_mask) +{ + int retval; + u32 saved_reg[8] = {0}; + + if (no_acfg_mask & CXADEC_NO_ACFG_AFE) { + saved_reg[0] = cx18_av_read4(cx, CXADEC_CHIP_CTRL); + saved_reg[1] = cx18_av_read4(cx, CXADEC_AFE_CTRL); + } + + if (no_acfg_mask & CXADEC_NO_ACFG_PLL) { + saved_reg[2] = cx18_av_read4(cx, CXADEC_PLL_CTRL1); + saved_reg[3] = cx18_av_read4(cx, CXADEC_VID_PLL_FRAC); + } + + if (no_acfg_mask & CXADEC_NO_ACFG_VID) { + saved_reg[4] = cx18_av_read4(cx, CXADEC_HORIZ_TIM_CTRL); + saved_reg[5] = cx18_av_read4(cx, CXADEC_VERT_TIM_CTRL); + saved_reg[6] = cx18_av_read4(cx, CXADEC_SRC_COMB_CFG); + saved_reg[7] = cx18_av_read4(cx, CXADEC_CHROMA_VBIOFF_CFG); + } + + retval = cx18_av_write(cx, addr, value); + + if (no_acfg_mask & CXADEC_NO_ACFG_AFE) { + cx18_av_write4(cx, CXADEC_CHIP_CTRL, saved_reg[0]); + cx18_av_write4(cx, CXADEC_AFE_CTRL, saved_reg[1]); + } + + if (no_acfg_mask & CXADEC_NO_ACFG_PLL) { + cx18_av_write4(cx, CXADEC_PLL_CTRL1, saved_reg[2]); + cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, saved_reg[3]); + } + + if (no_acfg_mask & CXADEC_NO_ACFG_VID) { + cx18_av_write4(cx, CXADEC_HORIZ_TIM_CTRL, saved_reg[4]); + cx18_av_write4(cx, CXADEC_VERT_TIM_CTRL, saved_reg[5]); + cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, saved_reg[6]); + cx18_av_write4(cx, CXADEC_CHROMA_VBIOFF_CFG, saved_reg[7]); + } + + return retval; +} + +int cx18_av_and_or_no_acfg(struct cx18 *cx, u16 addr, unsigned and_mask, + u8 or_value, int no_acfg_mask) +{ + return cx18_av_write_no_acfg(cx, addr, + (cx18_av_read(cx, addr) & and_mask) | + or_value, no_acfg_mask); +} + /* ----------------------------------------------------------------------- */ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, @@ -170,13 +222,15 @@ static void input_change(struct cx18 *cx) /* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */ if (std & V4L2_STD_SECAM) - cx18_av_write(cx, 0x402, 0); + cx18_av_write_no_acfg(cx, 0x402, 0, CXADEC_NO_ACFG_ALL); else { - cx18_av_write(cx, 0x402, 0x04); + cx18_av_write_no_acfg(cx, 0x402, 0x04, CXADEC_NO_ACFG_ALL); cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); } - cx18_av_and_or(cx, 0x401, ~0x60, 0); - cx18_av_and_or(cx, 0x401, ~0x60, 0x60); + cx18_av_and_or_no_acfg(cx, 0x401, ~0x60, 0, + CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID); + cx18_av_and_or_no_acfg(cx, 0x401, ~0x60, 0x60, + CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID); if (std & V4L2_STD_525_60) { if (std == V4L2_STD_NTSC_M_JP) { @@ -262,7 +316,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, cx18_av_write(cx, 0x103, reg); /* Set INPUT_MODE to Composite (0) or S-Video (1) */ - cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02); + cx18_av_and_or_no_acfg(cx, 0x401, ~0x6, is_composite ? 0 : 0x02, + CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID); /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */ @@ -318,12 +373,12 @@ static int set_v4lstd(struct cx18 *cx) This happens for example with the Yuan MPC622. */ if (fmt >= 4 && fmt < 8) { /* Set format to NTSC-M */ - cx18_av_and_or(cx, 0x400, ~0xf, 1); + cx18_av_and_or_no_acfg(cx, 0x400, ~0xf, 1, CXADEC_NO_ACFG_AFE); /* Turn off LCOMB */ cx18_av_and_or(cx, 0x47b, ~6, 0); } - cx18_av_and_or(cx, 0x400, ~0xf, fmt); - cx18_av_and_or(cx, 0x403, ~0x3, pal_m); + cx18_av_and_or_no_acfg(cx, 0x400, ~0xf, fmt, CXADEC_NO_ACFG_AFE); + cx18_av_and_or_no_acfg(cx, 0x403, ~0x3, pal_m, CXADEC_NO_ACFG_ALL); cx18_av_vbi_setup(cx); input_change(cx); return 0; diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h index 39f3c93..c172823 100644 --- a/drivers/media/video/cx18/cx18-av-core.h +++ b/drivers/media/video/cx18/cx18-av-core.h @@ -295,14 +295,24 @@ struct cx18_av_state { #define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */ #define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */ +/* Flags on what to preserve on write to 0x400-0x403 with cx18_av_.*_no_acfg()*/ +#define CXADEC_NO_ACFG_AFE 0x01 /* Preserve 0x100-0x107 */ +#define CXADEC_NO_ACFG_PLL 0x02 /* Preserve 0x108-0x10f */ +#define CXADEC_NO_ACFG_VID 0x04 /* Preserve 0x470-0x47f */ +#define CXADEC_NO_ACFG_ALL 0x07 + /* ----------------------------------------------------------------------- */ /* cx18_av-core.c */ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value); int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value); +int cx18_av_write_no_acfg(struct cx18 *cx, u16 addr, u8 value, + int no_acfg_mask); u8 cx18_av_read(struct cx18 *cx, u16 addr); u32 cx18_av_read4(struct cx18 *cx, u16 addr); int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value); int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value); +int cx18_av_and_or_no_acfg(struct cx18 *cx, u16 addr, unsigned mask, u8 value, + int no_acfg_mask); int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg); /* ----------------------------------------------------------------------- */ -- cgit v0.10.2 From 46195b555aa3edd265b4e765e4edff59b253b55e Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 19 Jun 2008 23:17:05 -0300 Subject: V4L/DVB (8066): cx18: Fix audio mux input definitions for HVR-1600 Line In 2 and FM radio Fix the cx18-cards.c structures for the HVR-1600 to reflect that audio Line In 2 and FM radio audio go to AIN3 and AIN4 of the CS5345 mux respectively. Verified by physical inspection of an HVR-1600MCE, by listening to FM broadcasts with the HVR-1600MCE, and by comparing with the card definition for a PVR-150 in ivtv. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index c35eb53..c18865b 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -67,10 +67,10 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL, CS5345_IN_2 }, { CX18_CARD_INPUT_LINE_IN2, - CX18_AV_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_3 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX18_AV_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_4 }, .ddr = { /* ESMT M13S128324A-5B memory */ .chip_config = 0x003, @@ -107,10 +107,10 @@ static const struct cx18_card cx18_card_hvr1600_samsung = { { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL, CS5345_IN_2 }, { CX18_CARD_INPUT_LINE_IN2, - CX18_AV_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_3 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX18_AV_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL, CS5345_IN_4 }, .ddr = { /* Samsung K4D263238G-VC33 memory */ .chip_config = 0x003, -- cgit v0.10.2 From 08cf7b2ed172cc83f3d2f44b712b3d54e6cc4ae6 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 22 Jun 2008 00:04:21 -0300 Subject: V4L/DVB (8067): cx18: Fix firmware load for case when digital capture happens first This is a fix for the case when a digital capture from dvr0 happens first after modprobe, before access to any cx18 v4l2 device nodes. The initial dvb feed start has been changed to load the firmware if not already loaded. Also fixed a use counter to correct dvb feed accounting if starting the transport DMA fails. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index c974417..cae3898 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/drivers/media/video/cx18/cx18-dvb.c @@ -69,11 +69,21 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) struct dvb_demux *demux = feed->demux; struct cx18_stream *stream = (struct cx18_stream *) demux->priv; struct cx18 *cx = stream->cx; - int ret = -EINVAL; + int ret; u32 v; CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n", feed->pid, feed->index); + + mutex_lock(&cx->serialize_lock); + ret = cx18_init_on_first_open(cx); + mutex_unlock(&cx->serialize_lock); + if (ret) { + CX18_ERR("Failed to initialize firmware starting DVB feed\n"); + return ret; + } + ret = -EINVAL; + switch (cx->card->type) { case CX18_CARD_HVR_1600_ESMT: case CX18_CARD_HVR_1600_SAMSUNG: @@ -101,6 +111,11 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) if (stream->dvb.feeding++ == 0) { CX18_DEBUG_INFO("Starting Transport DMA\n"); ret = cx18_start_v4l2_encode_stream(stream); + if (ret < 0) { + CX18_DEBUG_INFO( + "Failed to start Transport DMA\n"); + stream->dvb.feeding--; + } } else ret = 0; mutex_unlock(&stream->dvb.feedlock); -- cgit v0.10.2 From 1f09e8a25c9aaa4066b4593c1bf99a4cbcc38120 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 22 Jun 2008 01:27:00 -0300 Subject: V4L/DVB (8068): cx18: Add I2C slave reset via GPIO upon initialization cx18: Add I2C slave reset via GPIO upon initialization. One user, Michael , has reported this allows his HVR-1600 EEPROM to be consistently recognized when using (long,) 100 msec delays. The delays in this commit are nominal (10 & 40 msec) and need testing/tuning on boards with I2C problems to find the right values. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index c18865b..bbf23fa4 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -82,6 +82,11 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { }, .gpio_init.initial_value = 0x3001, .gpio_init.direction = 0x3001, + .gpio_i2c_slave_reset = { + .active_lo_mask = 0x3001, + .msecs_asserted = 10, + .msecs_recovery = 40, + }, .i2c = &cx18_i2c_std, }; @@ -122,6 +127,11 @@ static const struct cx18_card cx18_card_hvr1600_samsung = { }, .gpio_init.initial_value = 0x3001, .gpio_init.direction = 0x3001, + .gpio_i2c_slave_reset = { + .active_lo_mask = 0x3001, + .msecs_asserted = 10, + .msecs_recovery = 40, + }, .i2c = &cx18_i2c_std, }; diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index 3c2c0b9..dc2dd94 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -78,6 +78,13 @@ struct cx18_gpio_init { /* set initial GPIO DIR and OUT values */ u32 initial_value; }; +struct cx18_gpio_i2c_slave_reset { + u32 active_lo_mask; /* GPIO outputs that reset i2c chips when low */ + u32 active_hi_mask; /* GPIO outputs that reset i2c chips when high */ + int msecs_asserted; /* time period reset must remain asserted */ + int msecs_recovery; /* time after deassert for chips to be ready */ +}; + struct cx18_card_tuner { v4l2_std_id std; /* standard for which the tuner is suitable */ int tuner; /* tuner ID (from tuner.h) */ @@ -114,7 +121,8 @@ struct cx18_card { /* GPIO card-specific settings */ u8 xceive_pin; /* XCeive tuner GPIO reset pin */ - struct cx18_gpio_init gpio_init; + struct cx18_gpio_init gpio_init; + struct cx18_gpio_i2c_slave_reset gpio_i2c_slave_reset; struct cx18_card_tuner tuners[CX18_CARD_MAX_TUNERS]; struct cx18_card_tuner_i2c *i2c; diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c index ceb6365..b302833 100644 --- a/drivers/media/video/cx18/cx18-gpio.c +++ b/drivers/media/video/cx18/cx18-gpio.c @@ -53,10 +53,34 @@ static void gpio_write(struct cx18 *cx) write_reg(((dir & 0xffff) << 16) | (val & 0xffff), CX18_REG_GPIO_OUT1); write_reg(dir & 0xffff0000, CX18_REG_GPIO_DIR2); - write_reg((dir & 0xffff0000) | ((val & 0xffff0000) >> 16), + write_reg_sync((dir & 0xffff0000) | ((val & 0xffff0000) >> 16), CX18_REG_GPIO_OUT2); } +void cx18_reset_i2c_slaves_gpio(struct cx18 *cx) +{ + const struct cx18_gpio_i2c_slave_reset *p; + + p = &cx->card->gpio_i2c_slave_reset; + + if ((p->active_lo_mask | p->active_hi_mask) == 0) + return; + + /* Assuming that the masks are a subset of the bits in gpio_dir */ + + /* Assert */ + cx->gpio_val = + (cx->gpio_val | p->active_hi_mask) & ~(p->active_lo_mask); + gpio_write(cx); + schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted)); + + /* Deassert */ + cx->gpio_val = + (cx->gpio_val | p->active_lo_mask) & ~(p->active_hi_mask); + gpio_write(cx); + schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery)); +} + void cx18_gpio_init(struct cx18 *cx) { cx->gpio_dir = cx->card->gpio_init.direction; diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h index 41bac88..525c328 100644 --- a/drivers/media/video/cx18/cx18-gpio.h +++ b/drivers/media/video/cx18/cx18-gpio.h @@ -21,4 +21,5 @@ */ void cx18_gpio_init(struct cx18 *cx); +void cx18_reset_i2c_slaves_gpio(struct cx18 *cx); int cx18_reset_tuner_gpio(void *dev, int cmd, int value); diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index 1d6c51a..680bc4e 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -405,6 +405,8 @@ int init_cx18_i2c(struct cx18 *cx) cx18_setscl(&cx->i2c_algo_cb_data[1], 1); cx18_setsda(&cx->i2c_algo_cb_data[1], 1); + cx18_reset_i2c_slaves_gpio(cx); + return i2c_bit_add_bus(&cx->i2c_adap[0]) || i2c_bit_add_bus(&cx->i2c_adap[1]); } -- cgit v0.10.2 From ad907fa39517ca35b46912fbfe2b77cd89e1d56a Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 22 Jun 2008 02:00:09 -0300 Subject: V4L/DVB (8069): cx18: Fix S-Video and Compsite inputs for the Yuan MPC718 and enable card entry cx18: Fix S-Video and Compsite input settings for the Yuan MPC718 per user reports from Yuri Warczynski and Brian Hope and enable the card entry. The tuner reset GPIO pin is likely incorrect as the tuner firmware cannot be reloaded without a reboot. It is likely the audio routing is done via GPIO which is not implemented yet, as users report audio doesn't work for some inputs. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index bbf23fa4..c26e0ef 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -194,23 +194,26 @@ static const struct cx18_card_pci_info cx18_pci_mpc718[] = { static const struct cx18_card cx18_card_mpc718 = { .type = CX18_CARD_YUAN_MPC718, .name = "Yuan MPC718", - .comment = "Not yet supported!\n", - .v4l2_capabilities = 0, + .comment = "Some Composite and S-Video inputs are currently working.\n", + .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_all = CX18_HW_TUNER, .video_inputs = { - { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, - { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, - { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 }, + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, + { CX18_CARD_INPUT_SVIDEO1, 1, + CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, + { CX18_CARD_INPUT_SVIDEO2, 2, + CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 }, + { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 }, + { CX18_CARD_INPUT_COMPOSITE3, 2, CX18_AV_COMPOSITE3 }, }, .audio_inputs = { - { CX18_CARD_INPUT_AUD_TUNER, - CX18_AV_AUDIO8, 0 }, - { CX18_CARD_INPUT_LINE_IN1, - CX18_AV_AUDIO_SERIAL, 0 }, + { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, + { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL, 0 }, + { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL, 0 }, }, - .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX18_AV_AUDIO_SERIAL, 0 }, + .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO_SERIAL, 0 }, .tuners = { /* XC3028 tuner */ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, -- cgit v0.10.2 From c9fa2b1eee9d10c2455d3cd148cf13b34d91bdef Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Thu, 19 Jun 2008 22:45:55 -0300 Subject: V4L/DVB (8071): tda10023: Fix possible kernel oops during initialisation If the i2c write fails during initialisation, an oops happens because state->frontend.dvb is still undefined. Fixed. Thanks to Sigmund Augdal for reporting this bug, and to Hartmut Birr for suggesting the fix. Thanks-to: Sigmund Augdal Thanks-to: Hartmut Birr Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/tda10023.c b/drivers/media/dvb/frontends/tda10023.c index 0727b80..c6ff5b8 100644 --- a/drivers/media/dvb/frontends/tda10023.c +++ b/drivers/media/dvb/frontends/tda10023.c @@ -116,9 +116,12 @@ static u8 tda10023_readreg (struct tda10023_state* state, u8 reg) int ret; ret = i2c_transfer (state->i2c, msg, 2); - if (ret != 2) - printk("DVB: TDA10023: %s: readreg error (ret == %i)\n", - __func__, ret); + if (ret != 2) { + int num = state->frontend.dvb ? state->frontend.dvb->num : -1; + printk(KERN_ERR "DVB: TDA10023(%d): %s: readreg error " + "(reg == 0x%02x, ret == %i)\n", + num, __func__, reg, ret); + } return b1[0]; } @@ -129,11 +132,12 @@ static int tda10023_writereg (struct tda10023_state* state, u8 reg, u8 data) int ret; ret = i2c_transfer (state->i2c, &msg, 1); - if (ret != 1) - printk("DVB: TDA10023(%d): %s, writereg error " + if (ret != 1) { + int num = state->frontend.dvb ? state->frontend.dvb->num : -1; + printk(KERN_ERR "DVB: TDA10023(%d): %s, writereg error " "(reg == 0x%02x, val == 0x%02x, ret == %i)\n", - state->frontend.dvb->num, __func__, reg, data, ret); - + num, __func__, reg, data, ret); + } return (ret != 1) ? -EREMOTEIO : 0; } @@ -464,7 +468,7 @@ struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, int i; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda10023_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda10023_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ -- cgit v0.10.2 From edabaffc7e7fccdeadac6273704102adc2b604ae Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Thu, 19 Jun 2008 23:04:27 -0300 Subject: V4L/DVB (8073): av7110: Catch another type of ARM crash Catch another type of ARM crash. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c index 9d81074..c99936c 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.c +++ b/drivers/media/dvb/ttpci/av7110_hw.c @@ -427,6 +427,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) if (err) { printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n", __func__, type); + av7110->arm_errors++; return -ETIMEDOUT; } msleep(1); -- cgit v0.10.2 From 0b915e74ac10b65da930aa430837d4338f5deb65 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Thu, 19 Jun 2008 23:10:14 -0300 Subject: V4L/DVB (8074): av7110: OSD transfers should not be interrupted OSD transfers should not be interrupted. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c index c99936c..3a3f527 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.c +++ b/drivers/media/dvb/ttpci/av7110_hw.c @@ -854,10 +854,8 @@ static osd_raw_window_t bpp2bit[8] = { static inline int WaitUntilBmpLoaded(struct av7110 *av7110) { - int ret = wait_event_interruptible_timeout(av7110->bmpq, + int ret = wait_event_timeout(av7110->bmpq, av7110->bmp_state != BMP_LOADING, 10*HZ); - if (ret == -ERESTARTSYS) - return ret; if (ret == 0) { printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n", ret, av7110->bmp_state); -- cgit v0.10.2 From 7876ad75b1a3b7dc3d5d765d0be086d89fd2e663 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Thu, 19 Jun 2008 23:25:04 -0300 Subject: V4L/DVB (8075): stv0299: Uncorrected block count and bit error rate fixed Fix uncorrected block counter and bit error rate to follow DVB API spec: - Unsupported controls return -ENOSYS. - UNC must never be set to 0. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c index 1755618..35435be 100644 --- a/drivers/media/dvb/frontends/stv0299.c +++ b/drivers/media/dvb/frontends/stv0299.c @@ -63,6 +63,7 @@ struct stv0299_state { u32 symbol_rate; fe_code_rate_t fec_inner; int errmode; + u32 ucblocks; }; #define STATUS_BER 0 @@ -501,8 +502,10 @@ static int stv0299_read_ber(struct dvb_frontend* fe, u32* ber) { struct stv0299_state* state = fe->demodulator_priv; - if (state->errmode != STATUS_BER) return 0; - *ber = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e); + if (state->errmode != STATUS_BER) + return -ENOSYS; + + *ber = stv0299_readreg(state, 0x1e) | (stv0299_readreg(state, 0x1d) << 8); return 0; } @@ -540,8 +543,12 @@ static int stv0299_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { struct stv0299_state* state = fe->demodulator_priv; - if (state->errmode != STATUS_UCBLOCKS) *ucblocks = 0; - else *ucblocks = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e); + if (state->errmode != STATUS_UCBLOCKS) + return -ENOSYS; + + state->ucblocks += stv0299_readreg(state, 0x1e); + state->ucblocks += (stv0299_readreg(state, 0x1d) << 8); + *ucblocks = state->ucblocks; return 0; } -- cgit v0.10.2 From 7fa8e6fa1519194fc0c931f40d530fb55137bad9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 21 Jun 2008 13:23:27 -0300 Subject: V4L/DVB (8092): videodev: simplify and fix standard enumeration VIDIOC_ENUMSTD did not return all the PAL/SECAM/NTSC variants: it just returned one single PAL/SECAM/NTSC standard without separate entries for the trickier standards like NTSC-JP. Changed the code so that it behaves better. Also simplified the if/switch statements into a common standards lookup table. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 31e8af0..67a661c 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -51,12 +51,51 @@ #define VIDEO_NUM_DEVICES 256 #define VIDEO_NAME "video4linux" +struct std_descr { + v4l2_std_id std; + const char *descr; +}; + +static const struct std_descr standards[] = { + { V4L2_STD_NTSC, "NTSC" }, + { V4L2_STD_NTSC_M, "NTSC-M" }, + { V4L2_STD_NTSC_M_JP, "NTSC-M-JP" }, + { V4L2_STD_NTSC_M_KR, "NTSC-M-KR" }, + { V4L2_STD_NTSC_443, "NTSC-443" }, + { V4L2_STD_PAL, "PAL" }, + { V4L2_STD_PAL_BG, "PAL-BG" }, + { V4L2_STD_PAL_B, "PAL-B" }, + { V4L2_STD_PAL_B1, "PAL-B1" }, + { V4L2_STD_PAL_G, "PAL-G" }, + { V4L2_STD_PAL_H, "PAL-H" }, + { V4L2_STD_PAL_I, "PAL-I" }, + { V4L2_STD_PAL_DK, "PAL-DK" }, + { V4L2_STD_PAL_D, "PAL-D" }, + { V4L2_STD_PAL_D1, "PAL-D1" }, + { V4L2_STD_PAL_K, "PAL-K" }, + { V4L2_STD_PAL_M, "PAL-M" }, + { V4L2_STD_PAL_N, "PAL-N" }, + { V4L2_STD_PAL_Nc, "PAL-Nc" }, + { V4L2_STD_PAL_60, "PAL-60" }, + { V4L2_STD_SECAM, "SECAM" }, + { V4L2_STD_SECAM_B, "SECAM-B" }, + { V4L2_STD_SECAM_G, "SECAM-G" }, + { V4L2_STD_SECAM_H, "SECAM-H" }, + { V4L2_STD_SECAM_DK, "SECAM-DK" }, + { V4L2_STD_SECAM_D, "SECAM-D" }, + { V4L2_STD_SECAM_K, "SECAM-K" }, + { V4L2_STD_SECAM_K1, "SECAM-K1" }, + { V4L2_STD_SECAM_L, "SECAM-L" }, + { V4L2_STD_SECAM_LC, "SECAM-Lc" }, + { 0, "Unknown" } +}; + /* video4linux standard ID conversion to standard name */ -char *v4l2_norm_to_name(v4l2_std_id id) +const char *v4l2_norm_to_name(v4l2_std_id id) { - char *name; u32 myid = id; + int i; /* HACK: ppc32 architecture doesn't have __ucmpdi2 function to handle 64 bit comparations. So, on that architecture, with some gcc @@ -64,110 +103,17 @@ char *v4l2_norm_to_name(v4l2_std_id id) */ BUG_ON(myid != id); - switch (myid) { - case V4L2_STD_PAL: - name = "PAL"; - break; - case V4L2_STD_PAL_BG: - name = "PAL-BG"; - break; - case V4L2_STD_PAL_DK: - name = "PAL-DK"; - break; - case V4L2_STD_PAL_B: - name = "PAL-B"; - break; - case V4L2_STD_PAL_B1: - name = "PAL-B1"; - break; - case V4L2_STD_PAL_G: - name = "PAL-G"; - break; - case V4L2_STD_PAL_H: - name = "PAL-H"; - break; - case V4L2_STD_PAL_I: - name = "PAL-I"; - break; - case V4L2_STD_PAL_D: - name = "PAL-D"; - break; - case V4L2_STD_PAL_D1: - name = "PAL-D1"; - break; - case V4L2_STD_PAL_K: - name = "PAL-K"; - break; - case V4L2_STD_PAL_M: - name = "PAL-M"; - break; - case V4L2_STD_PAL_N: - name = "PAL-N"; - break; - case V4L2_STD_PAL_Nc: - name = "PAL-Nc"; - break; - case V4L2_STD_PAL_60: - name = "PAL-60"; - break; - case V4L2_STD_NTSC: - name = "NTSC"; - break; - case V4L2_STD_NTSC_M: - name = "NTSC-M"; - break; - case V4L2_STD_NTSC_M_JP: - name = "NTSC-M-JP"; - break; - case V4L2_STD_NTSC_443: - name = "NTSC-443"; - break; - case V4L2_STD_NTSC_M_KR: - name = "NTSC-M-KR"; - break; - case V4L2_STD_SECAM: - name = "SECAM"; - break; - case V4L2_STD_SECAM_DK: - name = "SECAM-DK"; - break; - case V4L2_STD_SECAM_B: - name = "SECAM-B"; - break; - case V4L2_STD_SECAM_D: - name = "SECAM-D"; - break; - case V4L2_STD_SECAM_G: - name = "SECAM-G"; - break; - case V4L2_STD_SECAM_H: - name = "SECAM-H"; - break; - case V4L2_STD_SECAM_K: - name = "SECAM-K"; - break; - case V4L2_STD_SECAM_K1: - name = "SECAM-K1"; - break; - case V4L2_STD_SECAM_L: - name = "SECAM-L"; - break; - case V4L2_STD_SECAM_LC: - name = "SECAM-LC"; - break; - default: - name = "Unknown"; - break; - } - - return name; + for (i = 0; standards[i].std; i++) + if (myid == standards[i].std) + break; + return standards[i].descr; } EXPORT_SYMBOL(v4l2_norm_to_name); /* Fill in the fields of a v4l2_standard structure according to the 'id' and 'transmission' parameters. Returns negative on error. */ int v4l2_video_std_construct(struct v4l2_standard *vs, - int id, char *name) + int id, const char *name) { u32 index = vs->index; @@ -1218,95 +1164,40 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_ENUMSTD: { struct v4l2_standard *p = arg; - v4l2_std_id id = vfd->tvnorms,curr_id=0; - unsigned int index = p->index,i; - - if (index<0) { - ret=-EINVAL; - break; - } - - /* Return norm array on a canonical way */ - for (i=0;i<= index && id; i++) { - if ( (id & V4L2_STD_PAL) == V4L2_STD_PAL) { - curr_id = V4L2_STD_PAL; - } else if ( (id & V4L2_STD_PAL_BG) == V4L2_STD_PAL_BG) { - curr_id = V4L2_STD_PAL_BG; - } else if ( (id & V4L2_STD_PAL_DK) == V4L2_STD_PAL_DK) { - curr_id = V4L2_STD_PAL_DK; - } else if ( (id & V4L2_STD_PAL_B) == V4L2_STD_PAL_B) { - curr_id = V4L2_STD_PAL_B; - } else if ( (id & V4L2_STD_PAL_B1) == V4L2_STD_PAL_B1) { - curr_id = V4L2_STD_PAL_B1; - } else if ( (id & V4L2_STD_PAL_G) == V4L2_STD_PAL_G) { - curr_id = V4L2_STD_PAL_G; - } else if ( (id & V4L2_STD_PAL_H) == V4L2_STD_PAL_H) { - curr_id = V4L2_STD_PAL_H; - } else if ( (id & V4L2_STD_PAL_I) == V4L2_STD_PAL_I) { - curr_id = V4L2_STD_PAL_I; - } else if ( (id & V4L2_STD_PAL_D) == V4L2_STD_PAL_D) { - curr_id = V4L2_STD_PAL_D; - } else if ( (id & V4L2_STD_PAL_D1) == V4L2_STD_PAL_D1) { - curr_id = V4L2_STD_PAL_D1; - } else if ( (id & V4L2_STD_PAL_K) == V4L2_STD_PAL_K) { - curr_id = V4L2_STD_PAL_K; - } else if ( (id & V4L2_STD_PAL_M) == V4L2_STD_PAL_M) { - curr_id = V4L2_STD_PAL_M; - } else if ( (id & V4L2_STD_PAL_N) == V4L2_STD_PAL_N) { - curr_id = V4L2_STD_PAL_N; - } else if ( (id & V4L2_STD_PAL_Nc) == V4L2_STD_PAL_Nc) { - curr_id = V4L2_STD_PAL_Nc; - } else if ( (id & V4L2_STD_PAL_60) == V4L2_STD_PAL_60) { - curr_id = V4L2_STD_PAL_60; - } else if ( (id & V4L2_STD_NTSC) == V4L2_STD_NTSC) { - curr_id = V4L2_STD_NTSC; - } else if ( (id & V4L2_STD_NTSC_M) == V4L2_STD_NTSC_M) { - curr_id = V4L2_STD_NTSC_M; - } else if ( (id & V4L2_STD_NTSC_M_JP) == V4L2_STD_NTSC_M_JP) { - curr_id = V4L2_STD_NTSC_M_JP; - } else if ( (id & V4L2_STD_NTSC_443) == V4L2_STD_NTSC_443) { - curr_id = V4L2_STD_NTSC_443; - } else if ( (id & V4L2_STD_NTSC_M_KR) == V4L2_STD_NTSC_M_KR) { - curr_id = V4L2_STD_NTSC_M_KR; - } else if ( (id & V4L2_STD_SECAM) == V4L2_STD_SECAM) { - curr_id = V4L2_STD_SECAM; - } else if ( (id & V4L2_STD_SECAM_DK) == V4L2_STD_SECAM_DK) { - curr_id = V4L2_STD_SECAM_DK; - } else if ( (id & V4L2_STD_SECAM_B) == V4L2_STD_SECAM_B) { - curr_id = V4L2_STD_SECAM_B; - } else if ( (id & V4L2_STD_SECAM_D) == V4L2_STD_SECAM_D) { - curr_id = V4L2_STD_SECAM_D; - } else if ( (id & V4L2_STD_SECAM_G) == V4L2_STD_SECAM_G) { - curr_id = V4L2_STD_SECAM_G; - } else if ( (id & V4L2_STD_SECAM_H) == V4L2_STD_SECAM_H) { - curr_id = V4L2_STD_SECAM_H; - } else if ( (id & V4L2_STD_SECAM_K) == V4L2_STD_SECAM_K) { - curr_id = V4L2_STD_SECAM_K; - } else if ( (id & V4L2_STD_SECAM_K1) == V4L2_STD_SECAM_K1) { - curr_id = V4L2_STD_SECAM_K1; - } else if ( (id & V4L2_STD_SECAM_L) == V4L2_STD_SECAM_L) { - curr_id = V4L2_STD_SECAM_L; - } else if ( (id & V4L2_STD_SECAM_LC) == V4L2_STD_SECAM_LC) { - curr_id = V4L2_STD_SECAM_LC; - } else { + v4l2_std_id id = vfd->tvnorms, curr_id = 0; + unsigned int index = p->index, i, j = 0; + const char *descr = ""; + + /* Return norm array in a canonical way */ + for (i = 0; i <= index && id; i++) { + /* last std value in the standards array is 0, so this + while always ends there since (id & 0) == 0. */ + while ((id & standards[j].std) != standards[j].std) + j++; + curr_id = standards[j].std; + descr = standards[j].descr; + j++; + if (curr_id == 0) break; - } - id &= ~curr_id; + if (curr_id != V4L2_STD_PAL && + curr_id != V4L2_STD_SECAM && + curr_id != V4L2_STD_NTSC) + id &= ~curr_id; } - if (i<=index) + if (i <= index) return -EINVAL; - v4l2_video_std_construct(p, curr_id,v4l2_norm_to_name(curr_id)); + v4l2_video_std_construct(p, curr_id, descr); p->index = index; - dbgarg (cmd, "index=%d, id=%Ld, name=%s, fps=%d/%d, " + dbgarg(cmd, "index=%d, id=%Ld, name=%s, fps=%d/%d, " "framelines=%d\n", p->index, (unsigned long long)p->id, p->name, p->frameperiod.numerator, p->frameperiod.denominator, p->framelines); - ret=0; + ret = 0; break; } case VIDIOC_G_STD: diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 33f01ae..859f7a6 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -40,9 +40,9 @@ #define VFL_TYPE_VTX 3 /* Video standard functions */ -extern char *v4l2_norm_to_name(v4l2_std_id id); +extern const char *v4l2_norm_to_name(v4l2_std_id id); extern int v4l2_video_std_construct(struct v4l2_standard *vs, - int id, char *name); + int id, const char *name); /* Prints the ioctl in a human-readable format */ extern void v4l_printk_ioctl(unsigned int cmd); -- cgit v0.10.2 From 836c28584d4629fdc85365f083b84e1298e14312 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Sat, 21 Jun 2008 19:32:41 -0300 Subject: V4L/DVB (8096): au8522: prevent false-positive lock status This decreases scan time in Queens, New York from 28 minutes to 7 minutes, with the exact same services found. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/au8522.c b/drivers/media/dvb/frontends/au8522.c index 978a287..03900d2 100644 --- a/drivers/media/dvb/frontends/au8522.c +++ b/drivers/media/dvb/frontends/au8522.c @@ -527,10 +527,8 @@ static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status) if (state->current_modulation == VSB_8) { dprintk("%s() Checking VSB_8\n", __func__); reg = au8522_readreg(state, 0x4088); - if (reg & 0x01) - *status |= FE_HAS_VITERBI; - if (reg & 0x02) - *status |= FE_HAS_LOCK | FE_HAS_SYNC; + if ((reg & 0x03) == 0x03) + *status |= FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI; } else { dprintk("%s() Checking QAM\n", __func__); reg = au8522_readreg(state, 0x4541); -- cgit v0.10.2 From e470d8177ed6ae56f4310ce793a57bcb2fed1749 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Sat, 21 Jun 2008 21:06:02 -0300 Subject: V4L/DVB (8097): xc5000: check device hardware state to determine if firmware download is needed This patch ensures that the xc5000 will have firmware loaded as needed if the part is powered down or reset via gpio from the host. An example of this, in some cases, could be after the system resumes from standby or hibernate modes. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index ceae6db..7cf4f5b 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -177,6 +177,7 @@ static XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = { {"FM Radio-INPUT1", 0x0208, 0x9002} }; +static int xc5000_is_firmware_loaded(struct dvb_frontend *fe); static int xc5000_writeregs(struct xc5000_priv *priv, u8 *buf, u8 len); static int xc5000_readregs(struct xc5000_priv *priv, u8 *buf, u8 len); static void xc5000_TunerReset(struct dvb_frontend *fe); @@ -352,7 +353,7 @@ static int xc_SetTVStandard(struct xc5000_priv *priv, static int xc_shutdown(struct xc5000_priv *priv) { - return 0; + return XC_RESULT_SUCCESS; /* Fixme: cannot bring tuner back alive once shutdown * without reloading the driver modules. * return xc_write_reg(priv, XREG_POWER_DOWN, 0); @@ -685,6 +686,25 @@ static int xc5000_set_params(struct dvb_frontend *fe, return 0; } +static int xc5000_is_firmware_loaded(struct dvb_frontend *fe) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret; + u16 id; + + ret = xc5000_readreg(priv, XREG_PRODUCT_ID, &id); + if (ret == XC_RESULT_SUCCESS) { + if (id == XC_PRODUCT_ID_FW_NOT_LOADED) + ret = XC_RESULT_RESET_FAILURE; + else + ret = XC_RESULT_SUCCESS; + } + + dprintk(1, "%s() returns %s id = 0x%x\n", __func__, + ret == XC_RESULT_SUCCESS ? "True" : "False", id); + return ret; +} + static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe); static int xc5000_set_analog_params(struct dvb_frontend *fe, @@ -693,7 +713,7 @@ static int xc5000_set_analog_params(struct dvb_frontend *fe, struct xc5000_priv *priv = fe->tuner_priv; int ret; - if(priv->fwloaded == 0) + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) xc_load_fw_and_init_tuner(fe); dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n", @@ -808,11 +828,10 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe) struct xc5000_priv *priv = fe->tuner_priv; int ret = 0; - if (priv->fwloaded == 0) { + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { ret = xc5000_fwupload(fe); if (ret != XC_RESULT_SUCCESS) return ret; - priv->fwloaded = 1; } /* Start the tuner self-calibration process */ @@ -852,7 +871,6 @@ static int xc5000_sleep(struct dvb_frontend *fe) return -EREMOTEIO; } else { - /* priv->fwloaded = 0; */ return XC_RESULT_SUCCESS; } } @@ -933,7 +951,6 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, cfg->i2c_address); printk(KERN_INFO "xc5000: Firmware has been loaded previously\n"); - priv->fwloaded = 1; break; case XC_PRODUCT_ID_FW_NOT_LOADED: printk(KERN_INFO @@ -941,7 +958,6 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, cfg->i2c_address); printk(KERN_INFO "xc5000: Firmware has not been loaded previously\n"); - priv->fwloaded = 0; break; default: printk(KERN_ERR diff --git a/drivers/media/common/tuners/xc5000_priv.h b/drivers/media/common/tuners/xc5000_priv.h index ecebfe4..a72a988 100644 --- a/drivers/media/common/tuners/xc5000_priv.h +++ b/drivers/media/common/tuners/xc5000_priv.h @@ -30,7 +30,6 @@ struct xc5000_priv { u32 bandwidth; u8 video_standard; u8 rf_mode; - u8 fwloaded; void *devptr; }; -- cgit v0.10.2 From 5c554e6b984ce6b36488b93a7ec8e2752233e7cb Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Sun, 22 Jun 2008 09:11:40 -0300 Subject: V4L/DVB (8100): V4L/vivi: fix possible memory leak in vivi_fillbuff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move allocation after first check and fix memory leak. Noticed-by: Daniel Marjamäki Signed-off-by: Marcin Slusarz Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 845be18..5ff9a58 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -327,13 +327,14 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) int hmax = buf->vb.height; int wmax = buf->vb.width; struct timeval ts; - char *tmpbuf = kmalloc(wmax * 2, GFP_ATOMIC); + char *tmpbuf; void *vbuf = videobuf_to_vmalloc(&buf->vb); - if (!tmpbuf) + if (!vbuf) return; - if (!vbuf) + tmpbuf = kmalloc(wmax * 2, GFP_ATOMIC); + if (!tmpbuf) return; for (h = 0; h < hmax; h++) { -- cgit v0.10.2 From a17898737eaed4ef41f273da7b830c632e06613e Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Sun, 22 Jun 2008 17:03:02 -0300 Subject: V4L/DVB (8108): Fix open/close race in saa7134 The saa7134 driver uses a (non-atomic) variable in an attempt to only allow one opener of the device (how it deals with sending the fd over unix sockets I don't know). Unfortunately, the release function first decrements this variable, and THEN goes on to disable more of the device. This allows for a race where another opener of the device comes in after the decrement of the variable, configures the hardware just to then see the hardware be disabled by the rest of the release function. This patch makes the release function use the same lock as the open function to protect the hardware as well as the variable (which now at least has some locking to protect it). Signed-off-by: Arjan van de Ven Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index 1c8cd0e..3ae71a3 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -110,6 +110,8 @@ static int ts_release(struct inode *inode, struct file *file) { struct saa7134_dev *dev = file->private_data; + mutex_lock(&dev->empress_tsq.vb_lock); + videobuf_stop(&dev->empress_tsq); videobuf_mmap_free(&dev->empress_tsq); @@ -122,6 +124,8 @@ static int ts_release(struct inode *inode, struct file *file) dev->empress_users--; + mutex_unlock(&dev->empress_tsq.vb_lock); + return 0; } -- cgit v0.10.2 From f471f92339860c35b561cf45ad563ab1ff07c386 Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Thu, 26 Jun 2008 16:06:19 +0200 Subject: s2io: fix documentation about intr_type The documentation for intr_type module parameter of the s2io driver is not consistent with the code. The comments in drivers/net/s2io.c are OK, but Documentation/networking/s2io.txt is wrong. Pointed out by Andrew Hecox. Signed-off-by: Michal Schmidt Signed-off-by: Jeff Garzik diff --git a/Documentation/networking/s2io.txt b/Documentation/networking/s2io.txt index 4bde53e..1e28e2d 100644 --- a/Documentation/networking/s2io.txt +++ b/Documentation/networking/s2io.txt @@ -83,9 +83,9 @@ Valid range: Limited by memory on system Default: 30 e. intr_type -Specifies interrupt type. Possible values 1(INTA), 2(MSI), 3(MSI-X) -Valid range: 1-3 -Default: 1 +Specifies interrupt type. Possible values 0(INTA), 2(MSI-X) +Valid values: 0, 2 +Default: 2 5. Performance suggestions General: -- cgit v0.10.2 From 59524a37446e18a672188d86d23c8c76fd488621 Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Wed, 25 Jun 2008 11:41:01 +0900 Subject: tc35815: Mark carrier-off before starting PHY Call netif_carrier_off() before starting PHY device. This is a behavior before converting to generic PHY layer. Signed-off-by: Atsushi Nemoto Signed-off-by: Jeff Garzik diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c index 10e4e85..dccea52 100644 --- a/drivers/net/tc35815.c +++ b/drivers/net/tc35815.c @@ -1394,6 +1394,7 @@ tc35815_open(struct net_device *dev) tc35815_chip_init(dev); spin_unlock_irq(&lp->lock); + netif_carrier_off(dev); /* schedule a link state check */ phy_start(lp->phy_dev); @@ -2453,6 +2454,7 @@ static int tc35815_resume(struct pci_dev *pdev) return 0; pci_set_power_state(pdev, PCI_D0); tc35815_restart(dev); + netif_carrier_off(dev); if (lp->phy_dev) phy_start(lp->phy_dev); netif_device_attach(dev); -- cgit v0.10.2 From ccc57aac9c9532b4540968632a8c4a0b946dbcc4 Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Thu, 26 Jun 2008 17:14:15 +0900 Subject: tc35815: Fix receiver hangup on Rx FIFO overflow On Rx FIFO overflow error, the controller consume a buffer descriptor but currently the driver does not give it back to the controller. This results unrecoverable 'Buffer List Exhausted' condition. This patch fix this problem by moving a "fbl_count--" line to proper place. Signed-off-by: Atsushi Nemoto Signed-off-by: Jeff Garzik diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c index dccea52..b07b8cb 100644 --- a/drivers/net/tc35815.c +++ b/drivers/net/tc35815.c @@ -1736,7 +1736,6 @@ tc35815_rx(struct net_device *dev) skb = lp->rx_skbs[cur_bd].skb; prefetch(skb->data); lp->rx_skbs[cur_bd].skb = NULL; - lp->fbl_count--; pci_unmap_single(lp->pci_dev, lp->rx_skbs[cur_bd].skb_dma, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); @@ -1792,6 +1791,7 @@ tc35815_rx(struct net_device *dev) #ifdef TC35815_USE_PACKEDBUFFER while (lp->fbl_curid != id) #else + lp->fbl_count--; while (lp->fbl_count < RX_BUF_NUM) #endif { -- cgit v0.10.2 From 6f4a0e45c6392f84436004d4c04d31b8ff5071c5 Mon Sep 17 00:00:00 2001 From: Paul Larson Date: Tue, 24 Jun 2008 17:00:56 -0700 Subject: ixgbe: fix EEH recovery during reset on PPC EEh is not recovering in a resonable amount of time on PPC during ixgbe_down(). Signed-off-by: Paul Larson Signed-off-by: Jesse Brandeburg Signed-off-by: Jeff Kirsher Signed-off-by: Jeff Garzik diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 7b85922..8f04609 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -1969,7 +1969,8 @@ void ixgbe_down(struct ixgbe_adapter *adapter) netif_carrier_off(netdev); netif_stop_queue(netdev); - ixgbe_reset(adapter); + if (!pci_channel_offline(adapter->pdev)) + ixgbe_reset(adapter); ixgbe_clean_all_tx_rings(adapter); ixgbe_clean_all_rx_rings(adapter); -- cgit v0.10.2 From 3023682e74bc17debc6aa5e234ae1d0b0e198719 Mon Sep 17 00:00:00 2001 From: Jeff Kirsher Date: Tue, 24 Jun 2008 17:01:15 -0700 Subject: igb: fix EEH recovery during reset on PPC EEH is not recovering in a reasonable amount of time on PPC during igb_down(). Signed-off-by: Jeff Kirsher Signed-off-by: Jesse Brandeburg Signed-off-by: Jeff Garzik diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index ae398f0..e79a26a 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -718,7 +718,8 @@ void igb_down(struct igb_adapter *adapter) adapter->link_speed = 0; adapter->link_duplex = 0; - igb_reset(adapter); + if (!pci_channel_offline(adapter->pdev)) + igb_reset(adapter); igb_clean_all_tx_rings(adapter); igb_clean_all_rx_rings(adapter); } -- cgit v0.10.2 From 52cc30862a8f90c98be8eb527d00e5e06d398b22 Mon Sep 17 00:00:00 2001 From: Jeff Kirsher Date: Tue, 24 Jun 2008 17:01:29 -0700 Subject: e1000e: fix EEH recovery during reset on PPC EEH is not recovering in a reasonable amount of time on PPC during e1000e_down(). Signed-off-by: Jeff Kirsher Signed-off-by: Jesse Brandeburg Signed-off-by: Jeff Garzik diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index cab1835..648a87b 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -2535,7 +2535,8 @@ void e1000e_down(struct e1000_adapter *adapter) adapter->link_speed = 0; adapter->link_duplex = 0; - e1000e_reset(adapter); + if (!pci_channel_offline(adapter->pdev)) + e1000e_reset(adapter); e1000_clean_tx_ring(adapter); e1000_clean_rx_ring(adapter); -- cgit v0.10.2 From 54299ef7e9ae4b5d47b02f3abea168cdc62a6f70 Mon Sep 17 00:00:00 2001 From: Komuro Date: Sat, 7 Jun 2008 21:37:56 +0900 Subject: pcnet_cs, axnet_cs: clear bogus interrupt before request_irq Signed-off-by: Komuro Signed-off-by: Jeff Garzik diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c index ce95c5d..70d012e 100644 --- a/drivers/net/pcmcia/axnet_cs.c +++ b/drivers/net/pcmcia/axnet_cs.c @@ -525,12 +525,14 @@ static int axnet_open(struct net_device *dev) int ret; axnet_dev_t *info = PRIV(dev); struct pcmcia_device *link = info->p_dev; + unsigned int nic_base = dev->base_addr; DEBUG(2, "axnet_open('%s')\n", dev->name); if (!pcmcia_dev_present(link)) return -ENODEV; + outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */ ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, "axnet_cs", dev); if (ret) return ret; diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index fd8158a..2d4c4ad 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -969,6 +969,7 @@ static int pcnet_open(struct net_device *dev) int ret; pcnet_dev_t *info = PRIV(dev); struct pcmcia_device *link = info->p_dev; + unsigned int nic_base = dev->base_addr; DEBUG(2, "pcnet_open('%s')\n", dev->name); @@ -976,6 +977,8 @@ static int pcnet_open(struct net_device *dev) return -ENODEV; set_misc_reg(dev); + + outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */ ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, dev_info, dev); if (ret) return ret; -- cgit v0.10.2 From 3f6602ad56dc538a846367bd6a05ac7ac4d3e641 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 23 Jun 2008 23:12:31 +0200 Subject: drivers/net/r6040.c: Eliminate double sizeof Taking sizeof the result of sizeof is quite strange and does not seem to be what is wanted here. This was fixed using the following semantic patch. (http://www.emn.fr/x-info/coccinelle/) // @@ expression E; @@ - sizeof ( sizeof (E) - ) // Signed-off-by: Julia Lawall Signed-off-by: Jeff Garzik diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c index 858b191..504a48f 100644 --- a/drivers/net/r6040.c +++ b/drivers/net/r6040.c @@ -273,7 +273,7 @@ static void r6040_init_ring_desc(struct r6040_descriptor *desc_ring, dma_addr_t mapping = desc_dma; while (size-- > 0) { - mapping += sizeof(sizeof(*desc)); + mapping += sizeof(*desc); desc->ndesc = cpu_to_le32(mapping); desc->vndescp = desc + 1; desc++; -- cgit v0.10.2 From ecfecfb5e39165b3f7f6d93aacd268edfe7c3524 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Mon, 23 Jun 2008 14:34:29 +0300 Subject: ipg: fix jumbo frame compilation Make jumbo frame support compile again. It was broken by the cleanup series before the merge because the code is hidden under JUMBO_FRAME #ifdef. Tested-by: Andrew Savchenko Signed-off-by: Pekka Enberg Signed-off-by: Jeff Garzik diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c index 679a082..dbb77e3 100644 --- a/drivers/net/ipg.c +++ b/drivers/net/ipg.c @@ -1271,7 +1271,7 @@ static void ipg_nic_rx_with_end(struct net_device *dev, framelen = le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFRAMELEN; - endframeLen = framelen - jumbo->current_size; + endframelen = framelen - jumbo->current_size; /* if (framelen > IPG_RXFRAG_SIZE) framelen=IPG_RXFRAG_SIZE; @@ -1279,8 +1279,8 @@ static void ipg_nic_rx_with_end(struct net_device *dev, if (framelen > IPG_RXSUPPORT_SIZE) dev_kfree_skb_irq(jumbo->skb); else { - memcpy(skb_put(jumbo->skb, endframeLen), - skb->data, endframeLen); + memcpy(skb_put(jumbo->skb, endframelen), + skb->data, endframelen); jumbo->skb->protocol = eth_type_trans(jumbo->skb, dev); @@ -1352,16 +1352,16 @@ static int ipg_nic_rx(struct net_device *dev) switch (ipg_nic_rx_check_frame_type(dev)) { case FRAME_WITH_START_WITH_END: - ipg_nic_rx_with_start_and_end(dev, tp, rxfd, entry); + ipg_nic_rx_with_start_and_end(dev, sp, rxfd, entry); break; case FRAME_WITH_START: - ipg_nic_rx_with_start(dev, tp, rxfd, entry); + ipg_nic_rx_with_start(dev, sp, rxfd, entry); break; case FRAME_WITH_END: - ipg_nic_rx_with_end(dev, tp, rxfd, entry); + ipg_nic_rx_with_end(dev, sp, rxfd, entry); break; case FRAME_NO_START_NO_END: - ipg_nic_rx_no_start_no_end(dev, tp, rxfd, entry); + ipg_nic_rx_no_start_no_end(dev, sp, rxfd, entry); break; } } -- cgit v0.10.2 From e8399fed7e9f2e76eb65852612b16732129b9f3f Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Mon, 23 Jun 2008 14:34:50 +0300 Subject: ipg: use NULL, not zero, for pointers Fixes a sparse warning in a code block that's hidden under JUMBO_FRAME #ifdef. Tested-by: Andrew Savchenko Signed-off-by: Pekka Enberg Signed-off-by: Jeff Garzik diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c index dbb77e3..2c03f4e 100644 --- a/drivers/net/ipg.c +++ b/drivers/net/ipg.c @@ -1808,7 +1808,7 @@ static int ipg_nic_open(struct net_device *dev) /* initialize JUMBO Frame control variable */ sp->jumbo.found_start = 0; sp->jumbo.current_size = 0; - sp->jumbo.skb = 0; + sp->jumbo.skb = NULL; dev->mtu = IPG_TXFRAG_SIZE; #endif -- cgit v0.10.2 From c5643cab7bf663ae049b11be43de8819683176dd Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 23 Jun 2008 10:41:23 +0200 Subject: [netdrvr] 3c59x: remove irqs_disabled warning from local_bh_enable Original Author: Michael Buesch net, vortex: fix lockup Ingo Molnar reported: -tip testing found that Johannes Berg's "softirq: remove irqs_disabled warning from local_bh_enable" enhancement to lockdep triggers a new warning on an old testbox that uses 3c59x vortex and netlogging: -----> calling vortex_init+0x0/0xb0 PCI: Found IRQ 10 for device 0000:00:0b.0 PCI: Sharing IRQ 10 with 0000:00:0a.0 PCI: Sharing IRQ 10 with 0000:00:0b.1 3c59x: Donald Becker and others. 0000:00:0b.0: 3Com PCI 3c556 Laptop Tornado at e0800400. PCI: Enabling bus mastering for device 0000:00:0b.0 initcall vortex_init+0x0/0xb0 returned 0 after 47 msecs ... calling init_netconsole+0x0/0x1b0 netconsole: local port 4444 netconsole: local IP 10.0.1.9 netconsole: interface eth0 netconsole: remote port 4444 netconsole: remote IP 10.0.1.16 netconsole: remote ethernet address 00:19:xx:xx:xx:xx netconsole: device eth0 not up yet, forcing it eth0: setting half-duplex. eth0: setting full-duplex. ------------[ cut here ]------------ WARNING: at kernel/softirq.c:137 local_bh_enable_ip+0xd1/0xe0() Pid: 1, comm: swapper Not tainted 2.6.26-rc6-tip #2091 [] warn_on_slowpath+0x4f/0x70 [] ? release_console_sem+0x1b4/0x1d0 [] ? vprintk+0x2a0/0x450 [] ? __mod_timer+0xa5/0xc0 [] ? mdio_sync+0x3d/0x50 [] ? marker_probe_cb+0x46/0xa0 [] ? printk+0x27/0x50 [] ? vortex_set_duplex+0x43/0xc0 [] ? vortex_set_duplex+0xa1/0xc0 [] ? vortex_timer+0xe2/0x3e0 [] local_bh_enable_ip+0xd1/0xe0 [] _spin_unlock_bh+0x2f/0x40 [] vortex_timer+0xe2/0x3e0 [] ? trace_hardirqs_on+0xb/0x10 [] ? trace_hardirqs_on_caller+0x88/0x160 [] run_timer_softirq+0x162/0x1c0 [] ? vortex_timer+0x0/0x3e0 [] local_bh_enable_ip+0xd1/0xe0 [] _spin_unlock_bh+0x2f/0x40 [] vortex_timer+0xe2/0x3e0 [] ? trace_hardirqs_on+0xb/0x10 [] ? trace_hardirqs_on_caller+0x88/0x160 [] run_timer_softirq+0x162/0x1c0 [] ? vortex_timer+0x0/0x3e0 [] ? vortex_timer+0x0/0x3e0 [] __do_softirq+0x9a/0x160 [] ? __do_softirq+0x0/0x160 [] call_on_stack+0x15/0x30 [] ? irq_exit+0x55/0x60 [] ? do_IRQ+0x85/0xd0 [] ? trace_hardirqs_on_caller+0xc1/0x160 [] ? common_interrupt+0x28/0x30 [] ? mutex_unlock+0x8/0x10 [] ? _cond_resched+0x10/0x30 [] ? netpoll_setup+0x117/0x390 [] ? init_netconsole+0x14e/0x1b0 [] ? ktime_get+0x19/0x40 [] ? kernel_init+0x1b2/0x2c0 [] ? init_netconsole+0x0/0x1b0 [] ? trace_hardirqs_on_thunk+0xc/0x10 [] ? restore_nocheck_notrace+0x0/0xe [] ? kernel_init+0x0/0x2c0 [] ? kernel_init+0x0/0x2c0 [] ? kernel_thread_helper+0x7/0x10 ======================= ---[ end trace 37f9c502aff112e0 ]--- console [netcon0] enabled netconsole: network logging started initcall init_netconsole+0x0/0x1b0 returned 0 after 2914 msecs looking at the driver I think the bug is real and the fix actually is trivial. vp->lock is also taken in hardware IRQ context, so we _have_ to always use irqsafe locking. As we run in a timer with IRQs disabled, we can simply use spin_lock. Cc: Signed-off-by: Ingo Molnar Signed-off-by: Jeff Garzik diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index 2edda8c..aabad8c 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -1768,9 +1768,10 @@ vortex_timer(unsigned long data) case XCVR_MII: case XCVR_NWAY: { ok = 1; - spin_lock_bh(&vp->lock); + /* Interrupts are already disabled */ + spin_lock(&vp->lock); vortex_check_media(dev, 0); - spin_unlock_bh(&vp->lock); + spin_unlock(&vp->lock); } break; default: /* Other media types handled by Tx timeouts. */ -- cgit v0.10.2 From 70081ac55df939363b27c1ebd27c51f510129139 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 23 Jun 2008 02:04:50 +0100 Subject: [netdrvr] netxen: fix netxen_pci_tbl[] breakage PCI_DEVICE_CLASS sets .device and .vendor to PCI_ANY_DEV, which overrides the effect of preceding PCI_DEVICE() and makes all elements of netxen_pci_tbl[] identical. Introduced in the commit dcd56fdbaeae1008044687b973c4a3e852e8a726. Signed-off-by: Al Viro Signed-off-by: Jeff Garzik diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 6797ed0..63cd67b 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -71,14 +71,18 @@ static irqreturn_t netxen_intr(int irq, void *data); static irqreturn_t netxen_msi_intr(int irq, void *data); /* PCI Device ID Table */ +#define ENTRY(device) \ + {PCI_DEVICE(0x4040, (device)), \ + .class = PCI_CLASS_NETWORK_ETHERNET << 8, .class_mask = ~0} + static struct pci_device_id netxen_pci_tbl[] __devinitdata = { - {PCI_DEVICE(0x4040, 0x0001), PCI_DEVICE_CLASS(0x020000, ~0)}, - {PCI_DEVICE(0x4040, 0x0002), PCI_DEVICE_CLASS(0x020000, ~0)}, - {PCI_DEVICE(0x4040, 0x0003), PCI_DEVICE_CLASS(0x020000, ~0)}, - {PCI_DEVICE(0x4040, 0x0004), PCI_DEVICE_CLASS(0x020000, ~0)}, - {PCI_DEVICE(0x4040, 0x0005), PCI_DEVICE_CLASS(0x020000, ~0)}, - {PCI_DEVICE(0x4040, 0x0024), PCI_DEVICE_CLASS(0x020000, ~0)}, - {PCI_DEVICE(0x4040, 0x0025), PCI_DEVICE_CLASS(0x020000, ~0)}, + ENTRY(0x0001), + ENTRY(0x0002), + ENTRY(0x0003), + ENTRY(0x0004), + ENTRY(0x0005), + ENTRY(0x0024), + ENTRY(0x0025), {0,} }; -- cgit v0.10.2 From 1923815d855e1daec931fc9f2221fb73ca708870 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Sat, 21 Jun 2008 18:20:35 +0800 Subject: e100: Do pci_dma_sync after skb_alloc for proper operation on ixp4xx The E100 device can't work on current kernel (2.6.26-rc6) and will cause kernel corruption on intel ixdp4xx. Signed-off-by: Jeff Garzik diff --git a/drivers/net/e100.c b/drivers/net/e100.c index f3cba5e..1037b13 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -1803,6 +1803,8 @@ static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx) if (rx->prev->skb) { struct rfd *prev_rfd = (struct rfd *)rx->prev->skb->data; put_unaligned_le32(rx->dma_addr, &prev_rfd->link); + pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr, + sizeof(struct rfd), PCI_DMA_TODEVICE); } return 0; -- cgit v0.10.2 From 581abbc26a7adb693fb8b913f1be18d1c349c1ab Mon Sep 17 00:00:00 2001 From: Andy Gospodarek Date: Thu, 19 Jun 2008 17:19:02 -0400 Subject: e1000: only enable TSO6 via ethtool when using correct hardware When enabling TSO via ethool on e1000, it is possible to set NETIF_F_TSO6 on hardware that does not support it. Setting TSO via ethtool now matches the settings used when the hardware is probed. Signed-off-by: Andy Gospodarek Signed-off-by: Jeff Garzik diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index 701531e..a3f6a9c 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c @@ -347,7 +347,7 @@ e1000_set_tso(struct net_device *netdev, u32 data) else netdev->features &= ~NETIF_F_TSO; - if (data) + if (data && (adapter->hw.mac_type > e1000_82547_rev_2)) netdev->features |= NETIF_F_TSO6; else netdev->features &= ~NETIF_F_TSO6; -- cgit v0.10.2 From 64c42f697661e27c9688a32c1ba61d0228e81d84 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 18 Jun 2008 13:58:36 +0200 Subject: [netdrvr] Fix IOMMU overflow checking in s2io.c s2io has IOMMU overflow checking, but unfortunately it is wrong. It didn't use the standard macros, which meant that it only worked on POWER and SPARC because only those define DMA_ERROR_CODE. Convert it to use the standard macros instead. I also commented two more bugs in the IOMMU handling. It assumes that 0 DMA addresses cannot happen, but that's not true in all IOMMU setups. The information if a buffer has been already mapped needs to be stored elsewhere. Didn't fix those because it needs careful checking of the buffer handling by the maintainers. Cc: ram.vepa@neterion.com Cc: santosh.rastapur@neterion.com Cc: sivakumar.subramani@neterion.com Cc: sreenivasa.honnur@neterion.com Signed-off-by: Andi Kleen Signed-off-by: Jeff Garzik diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index b5c1e66..ae7b697 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -2625,9 +2625,7 @@ static int fill_rx_buffers(struct ring_info *ring) rxdp1->Buffer0_ptr = pci_map_single (ring->pdev, skb->data, size - NET_IP_ALIGN, PCI_DMA_FROMDEVICE); - if( (rxdp1->Buffer0_ptr == 0) || - (rxdp1->Buffer0_ptr == - DMA_ERROR_CODE)) + if(pci_dma_mapping_error(rxdp1->Buffer0_ptr)) goto pci_map_failed; rxdp->Control_2 = @@ -2657,6 +2655,7 @@ static int fill_rx_buffers(struct ring_info *ring) skb->data = (void *) (unsigned long)tmp; skb_reset_tail_pointer(skb); + /* AK: check is wrong. 0 can be valid dma address */ if (!(rxdp3->Buffer0_ptr)) rxdp3->Buffer0_ptr = pci_map_single(ring->pdev, ba->ba_0, @@ -2665,8 +2664,7 @@ static int fill_rx_buffers(struct ring_info *ring) pci_dma_sync_single_for_device(ring->pdev, (dma_addr_t) rxdp3->Buffer0_ptr, BUF0_LEN, PCI_DMA_FROMDEVICE); - if( (rxdp3->Buffer0_ptr == 0) || - (rxdp3->Buffer0_ptr == DMA_ERROR_CODE)) + if (pci_dma_mapping_error(rxdp3->Buffer0_ptr)) goto pci_map_failed; rxdp->Control_2 = SET_BUFFER0_SIZE_3(BUF0_LEN); @@ -2681,18 +2679,17 @@ static int fill_rx_buffers(struct ring_info *ring) (ring->pdev, skb->data, ring->mtu + 4, PCI_DMA_FROMDEVICE); - if( (rxdp3->Buffer2_ptr == 0) || - (rxdp3->Buffer2_ptr == DMA_ERROR_CODE)) + if (pci_dma_mapping_error(rxdp3->Buffer2_ptr)) goto pci_map_failed; + /* AK: check is wrong */ if (!rxdp3->Buffer1_ptr) rxdp3->Buffer1_ptr = pci_map_single(ring->pdev, ba->ba_1, BUF1_LEN, PCI_DMA_FROMDEVICE); - if( (rxdp3->Buffer1_ptr == 0) || - (rxdp3->Buffer1_ptr == DMA_ERROR_CODE)) { + if (pci_dma_mapping_error(rxdp3->Buffer1_ptr)) { pci_unmap_single (ring->pdev, (dma_addr_t)(unsigned long) @@ -4264,16 +4261,14 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev) txdp->Buffer_Pointer = pci_map_single(sp->pdev, fifo->ufo_in_band_v, sizeof(u64), PCI_DMA_TODEVICE); - if((txdp->Buffer_Pointer == 0) || - (txdp->Buffer_Pointer == DMA_ERROR_CODE)) + if (pci_dma_mapping_error(txdp->Buffer_Pointer)) goto pci_map_failed; txdp++; } txdp->Buffer_Pointer = pci_map_single (sp->pdev, skb->data, frg_len, PCI_DMA_TODEVICE); - if((txdp->Buffer_Pointer == 0) || - (txdp->Buffer_Pointer == DMA_ERROR_CODE)) + if (pci_dma_mapping_error(txdp->Buffer_Pointer)) goto pci_map_failed; txdp->Host_Control = (unsigned long) skb; @@ -6884,10 +6879,8 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, pci_map_single( sp->pdev, (*skb)->data, size - NET_IP_ALIGN, PCI_DMA_FROMDEVICE); - if( (rxdp1->Buffer0_ptr == 0) || - (rxdp1->Buffer0_ptr == DMA_ERROR_CODE)) { + if (pci_dma_mapping_error(rxdp1->Buffer0_ptr)) goto memalloc_failed; - } rxdp->Host_Control = (unsigned long) (*skb); } } else if ((sp->rxd_mode == RXD_MODE_3B) && (rxdp->Host_Control == 0)) { @@ -6913,15 +6906,12 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, pci_map_single(sp->pdev, (*skb)->data, dev->mtu + 4, PCI_DMA_FROMDEVICE); - if( (rxdp3->Buffer2_ptr == 0) || - (rxdp3->Buffer2_ptr == DMA_ERROR_CODE)) { + if (pci_dma_mapping_error(rxdp3->Buffer2_ptr)) goto memalloc_failed; - } rxdp3->Buffer0_ptr = *temp0 = pci_map_single( sp->pdev, ba->ba_0, BUF0_LEN, PCI_DMA_FROMDEVICE); - if( (rxdp3->Buffer0_ptr == 0) || - (rxdp3->Buffer0_ptr == DMA_ERROR_CODE)) { + if (pci_dma_mapping_error(rxdp3->Buffer0_ptr)) { pci_unmap_single (sp->pdev, (dma_addr_t)rxdp3->Buffer2_ptr, dev->mtu + 4, PCI_DMA_FROMDEVICE); @@ -6933,8 +6923,7 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, rxdp3->Buffer1_ptr = *temp1 = pci_map_single(sp->pdev, ba->ba_1, BUF1_LEN, PCI_DMA_FROMDEVICE); - if( (rxdp3->Buffer1_ptr == 0) || - (rxdp3->Buffer1_ptr == DMA_ERROR_CODE)) { + if (pci_dma_mapping_error(rxdp3->Buffer1_ptr)) { pci_unmap_single (sp->pdev, (dma_addr_t)rxdp3->Buffer0_ptr, BUF0_LEN, PCI_DMA_FROMDEVICE); diff --git a/drivers/net/s2io.h b/drivers/net/s2io.h index 4706f7f..1827b66 100644 --- a/drivers/net/s2io.h +++ b/drivers/net/s2io.h @@ -75,10 +75,6 @@ static int debug_level = ERR_DBG; /* DEBUG message print. */ #define DBG_PRINT(dbg_level, args...) if(!(debug_level Date: Tue, 6 May 2008 19:36:26 +0100 Subject: qla3xxx: Hold RTNL while calling dev_close() dev_close() must be called holding the RTNL. Compile-tested only. Signed-off-by: Ben Hutchings Signed-off-by: Jeff Garzik diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c index b7f7b22..bccee68 100644 --- a/drivers/net/qla3xxx.c +++ b/drivers/net/qla3xxx.c @@ -3701,7 +3701,9 @@ static int ql_cycle_adapter(struct ql3_adapter *qdev, int reset) printk(KERN_ERR PFX "%s: Driver up/down cycle failed, " "closing device\n",qdev->ndev->name); + rtnl_lock(); dev_close(qdev->ndev); + rtnl_unlock(); return -1; } return 0; -- cgit v0.10.2 From 3e3cda96d014b69f7723d1d4507897e5be6aceb7 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 6 May 2008 19:41:48 +0100 Subject: Hold RTNL while calling dev_close() dev_close() must be called holding the RTNL. Compile-tested only. Signed-off-by: Ben Hutchings Signed-off-by: Jeff Garzik diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index 249e180..069f8bb 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "x25_asy.h" #include @@ -601,8 +602,10 @@ static void x25_asy_close_tty(struct tty_struct *tty) if (!sl || sl->magic != X25_ASY_MAGIC) return; + rtnl_lock(); if (sl->dev->flags & IFF_UP) dev_close(sl->dev); + rtnl_unlock(); tty->disc_data = NULL; sl->tty = NULL; -- cgit v0.10.2 From 70a3143af87c6ca188107cbd49ab5eec2c86c456 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 21 Jun 2008 16:07:32 +0900 Subject: sata_uli: hardreset is broken sata_uli can't do hardresets reliably and lock up. This went unnoticed till now as softreset was the default and hardreset was only used after softreset failed. Reported by Christian Casteyde in bz#10860. Signed-off-by: Tejun Heo Cc: Christian Casteyde Signed-off-by: Jeff Garzik diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c index f277cea..db529b8 100644 --- a/drivers/ata/sata_uli.c +++ b/drivers/ata/sata_uli.c @@ -83,6 +83,7 @@ static struct ata_port_operations uli_ops = { .inherits = &ata_bmdma_port_ops, .scr_read = uli_scr_read, .scr_write = uli_scr_write, + .hardreset = ATA_OP_NULL, }; static const struct ata_port_info uli_port_info = { -- cgit v0.10.2 From 980dfcb93232907034a2c92d62d3a7d6ac7bef44 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Wed, 25 Jun 2008 21:27:00 +0200 Subject: rt2x00: Fix lock dependency errror This fixes a circular locking dependency in the workqueue handling. The interface work task uses the mac80211 function ieee80211_iterate_active_interfaces() which grabs the RTNL lock. However when the interface is brough down, this happens under the RTNL lock as well, this causes problems because mac80211 will flush the workqueue during the ifdown event. This causes mac80211 to wait until the driver has completed all work which can't finish because it is waiting on the RTNL lock. This is fixed by moving rt2x00 workqueue tasks on a different workqueue, this workqueue can be flushed when the ieee80211_hw structure is removed by the driver (when the driver is unloaded) which does not happen under the RTNL lock. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 611d983..b4bf1e0 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -821,6 +821,7 @@ struct rt2x00_dev { /* * Scheduled work. */ + struct workqueue_struct *workqueue; struct work_struct intf_work; struct work_struct filter_work; diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 2673d56..c997d4f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -75,7 +75,7 @@ static void rt2x00lib_start_link_tuner(struct rt2x00_dev *rt2x00dev) rt2x00lib_reset_link_tuner(rt2x00dev); - queue_delayed_work(rt2x00dev->hw->workqueue, + queue_delayed_work(rt2x00dev->workqueue, &rt2x00dev->link.work, LINK_TUNE_INTERVAL); } @@ -137,14 +137,6 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev) return; /* - * Stop all scheduled work. - */ - if (work_pending(&rt2x00dev->intf_work)) - cancel_work_sync(&rt2x00dev->intf_work); - if (work_pending(&rt2x00dev->filter_work)) - cancel_work_sync(&rt2x00dev->filter_work); - - /* * Stop the TX queues. */ ieee80211_stop_queues(rt2x00dev->hw); @@ -398,8 +390,8 @@ static void rt2x00lib_link_tuner(struct work_struct *work) * Increase tuner counter, and reschedule the next link tuner run. */ rt2x00dev->link.count++; - queue_delayed_work(rt2x00dev->hw->workqueue, &rt2x00dev->link.work, - LINK_TUNE_INTERVAL); + queue_delayed_work(rt2x00dev->workqueue, + &rt2x00dev->link.work, LINK_TUNE_INTERVAL); } static void rt2x00lib_packetfilter_scheduled(struct work_struct *work) @@ -433,6 +425,15 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac, spin_unlock(&intf->lock); + /* + * It is possible the radio was disabled while the work had been + * scheduled. If that happens we should return here immediately, + * note that in the spinlock protected area above the delayed_flags + * have been cleared correctly. + */ + if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + if (delayed_flags & DELAYED_UPDATE_BEACON) { skb = ieee80211_beacon_get(rt2x00dev->hw, vif, &control); if (skb && rt2x00dev->ops->hw->beacon_update(rt2x00dev->hw, @@ -441,7 +442,7 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac, } if (delayed_flags & DELAYED_CONFIG_ERP) - rt2x00lib_config_erp(rt2x00dev, intf, &intf->conf); + rt2x00lib_config_erp(rt2x00dev, intf, &conf); if (delayed_flags & DELAYED_LED_ASSOC) rt2x00leds_led_assoc(rt2x00dev, !!rt2x00dev->intf_associated); @@ -487,7 +488,7 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) rt2x00lib_beacondone_iter, rt2x00dev); - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->intf_work); + queue_work(rt2x00dev->workqueue, &rt2x00dev->intf_work); } EXPORT_SYMBOL_GPL(rt2x00lib_beacondone); @@ -1130,6 +1131,10 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) /* * Initialize configuration work. */ + rt2x00dev->workqueue = create_singlethread_workqueue("rt2x00lib"); + if (!rt2x00dev->workqueue) + goto exit; + INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); INIT_WORK(&rt2x00dev->filter_work, rt2x00lib_packetfilter_scheduled); INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00lib_link_tuner); @@ -1190,6 +1195,13 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) rt2x00leds_unregister(rt2x00dev); /* + * Stop all queued work. Note that most tasks will already be halted + * during rt2x00lib_disable_radio() and rt2x00lib_uninitialize(). + */ + flush_workqueue(rt2x00dev->workqueue); + destroy_workqueue(rt2x00dev->workqueue); + + /* * Free ieee80211_hw memory. */ rt2x00lib_remove_hw(rt2x00dev); diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 87e280a..9cb023e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -428,7 +428,7 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw, if (!test_bit(DRIVER_REQUIRE_SCHEDULED, &rt2x00dev->flags)) rt2x00dev->ops->lib->config_filter(rt2x00dev, *total_flags); else - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->filter_work); + queue_work(rt2x00dev->workqueue, &rt2x00dev->filter_work); } EXPORT_SYMBOL_GPL(rt2x00mac_configure_filter); @@ -509,7 +509,7 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, memcpy(&intf->conf, bss_conf, sizeof(*bss_conf)); if (delayed) { intf->delayed_flags |= delayed; - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->intf_work); + queue_work(rt2x00dev->workqueue, &rt2x00dev->intf_work); } spin_unlock(&intf->lock); } -- cgit v0.10.2 From 5f4a6fae46a214c4dce3bd63a6219a5f1c818c78 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Wed, 25 Jun 2008 14:20:37 -0700 Subject: prism: islpci_eth.c endianness fix clock is already cpu-endian (see le32_to_cpu slightly before), so le64_to_cpu doesn't make much sense. Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/prism54/islpci_eth.c b/drivers/net/wireless/prism54/islpci_eth.c index 762e85b..e43bae9 100644 --- a/drivers/net/wireless/prism54/islpci_eth.c +++ b/drivers/net/wireless/prism54/islpci_eth.c @@ -290,7 +290,7 @@ islpci_monitor_rx(islpci_private *priv, struct sk_buff **skb) avs->version = cpu_to_be32(P80211CAPTURE_VERSION); avs->length = cpu_to_be32(sizeof (struct avs_80211_1_header)); - avs->mactime = cpu_to_be64(le64_to_cpu(clock)); + avs->mactime = cpu_to_be64(clock); avs->hosttime = cpu_to_be64(jiffies); avs->phytype = cpu_to_be32(6); /*OFDM: 6 for (g), 8 for (a) */ avs->channel = cpu_to_be32(channel_of_freq(freq)); -- cgit v0.10.2 From 00eb7fe77eb455f807c396f9917f0f623d4c84bb Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 26 Jun 2008 12:13:46 +0300 Subject: mac80211: fix an oops in several failure paths in key allocation This patch fixes an oops in several failure paths in key allocation. This Oops occurs when freeing a key that has not been linked yet, so the key->sdata is not set. Signed-off-by: Emmanuel Grumbach Signed-off-by: Tomas Winkler Acked-by: Johannes Berg Signed-off-by: John W. Linville diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 150d66d..220e83b 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -380,6 +380,15 @@ void ieee80211_key_free(struct ieee80211_key *key) if (!key) return; + if (!key->sdata) { + /* The key has not been linked yet, simply free it + * and don't Oops */ + if (key->conf.alg == ALG_CCMP) + ieee80211_aes_key_free(key->u.ccmp.tfm); + kfree(key); + return; + } + spin_lock_irqsave(&key->sdata->local->key_lock, flags); __ieee80211_key_free(key); spin_unlock_irqrestore(&key->sdata->local->key_lock, flags); -- cgit v0.10.2 From 0e3e2eabf4fbc0162e1f1eb4fd90cb3e9513a554 Mon Sep 17 00:00:00 2001 From: Richard Sharpe Date: Tue, 24 Jun 2008 19:11:13 -0700 Subject: firewire: fw-sbp2: fix parsing of logical unit directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a small off-by-one bug in firewire-sbp2. This causes problems when a device exports multiple LUN Directories. I found it when trying to talk to a SONY DVD Jukebox. Signed-off-by: Richard Sharpe Acked-by: Kristian Høgsberg Signed-off-by: Stefan Richter (op. order, changelog) diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index b2458bb..227d2e0 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -1051,7 +1051,8 @@ static int sbp2_scan_unit_dir(struct sbp2_target *tgt, u32 *directory, break; case SBP2_CSR_LOGICAL_UNIT_DIRECTORY: - if (sbp2_scan_logical_unit_dir(tgt, ci.p + value) < 0) + /* Adjust for the increment in the iterator */ + if (sbp2_scan_logical_unit_dir(tgt, ci.p - 1 + value) < 0) return -ENOMEM; break; } -- cgit v0.10.2 From b660398101cd0622325480a67ac88bb4d33d553a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 27 Jun 2008 14:39:42 +0100 Subject: kbuild: fix a.out.h export to userspace with O= build. We need to check for existence of the a.out.h header in the source tree, not the object tree, if we want it to get the right answer with O=. Signed-off-by: David Woodhouse Signed-off-by: Sam Ravnborg diff --git a/include/asm-generic/Kbuild.asm b/include/asm-generic/Kbuild.asm index 92a6d91..7cd25b8 100644 --- a/include/asm-generic/Kbuild.asm +++ b/include/asm-generic/Kbuild.asm @@ -1,6 +1,6 @@ header-y += kvm.h -ifeq ($(wildcard include/asm-$(SRCARCH)/a.out.h),include/asm-$(SRCARCH)/a.out.h) +ifneq ($(wildcard $(srctree)/include/asm-$(SRCARCH)/a.out.h),) unifdef-y += a.out.h endif unifdef-y += auxvec.h diff --git a/include/asm-powerpc/Kbuild b/include/asm-powerpc/Kbuild index 7381916..bca352e 100644 --- a/include/asm-powerpc/Kbuild +++ b/include/asm-powerpc/Kbuild @@ -1,6 +1,5 @@ include include/asm-generic/Kbuild.asm -header-y += a.out.h header-y += auxvec.h header-y += ioctls.h header-y += mman.h diff --git a/include/linux/Kbuild b/include/linux/Kbuild index b6fbb25..71d70d1 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -166,7 +166,7 @@ unifdef-y += acct.h unifdef-y += adb.h unifdef-y += adfs_fs.h unifdef-y += agpgart.h -ifeq ($(wildcard include/asm-$(SRCARCH)/a.out.h),include/asm-$(SRCARCH)/a.out.h) +ifneq ($(wildcard $(srctree)/include/asm-$(SRCARCH)/a.out.h),) unifdef-y += a.out.h endif unifdef-y += apm_bios.h -- cgit v0.10.2 From 479798211967cd828e09ce27775b8cbfe99462ab Mon Sep 17 00:00:00 2001 From: Andre Haupt Date: Fri, 27 Jun 2008 17:22:08 -0700 Subject: hamradio: remove unused variable Signed-off-by: Andre Haupt Signed-off-by: David S. Miller diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index 0b94833..e8cfade 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -1077,8 +1077,6 @@ static inline void rx_off(struct scc_priv *priv) static void start_timer(struct scc_priv *priv, int t, int r15) { - unsigned long flags; - outb(priv->tmr_mode, priv->tmr_ctrl); if (t == 0) { tm_isr(priv); -- cgit v0.10.2 From 57413ebc4e0f1e471a3b4db4aff9a85c083d090e Mon Sep 17 00:00:00 2001 From: Miquel van Smoorenburg Date: Fri, 27 Jun 2008 17:23:57 -0700 Subject: tcp: calculate tcp_mem based on low memory instead of all memory The tcp_mem array which contains limits on the total amount of memory used by TCP sockets is calculated based on nr_all_pages. On a 32 bits x86 system, we should base this on the number of lowmem pages. Signed-off-by: Miquel van Smoorenburg Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index fc54a48..850825d 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -260,6 +260,8 @@ #include #include #include +#include +#include #include #include #include @@ -2620,7 +2622,7 @@ __setup("thash_entries=", set_thash_entries); void __init tcp_init(void) { struct sk_buff *skb = NULL; - unsigned long limit; + unsigned long nr_pages, limit; int order, i, max_share; BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > sizeof(skb->cb)); @@ -2689,8 +2691,9 @@ void __init tcp_init(void) * is up to 1/2 at 256 MB, decreasing toward zero with the amount of * memory, with a floor of 128 pages. */ - limit = min(nr_all_pages, 1UL<<(28-PAGE_SHIFT)) >> (20-PAGE_SHIFT); - limit = (limit * (nr_all_pages >> (20-PAGE_SHIFT))) >> (PAGE_SHIFT-11); + nr_pages = totalram_pages - totalhigh_pages; + limit = min(nr_pages, 1UL<<(28-PAGE_SHIFT)) >> (20-PAGE_SHIFT); + limit = (limit * (nr_pages >> (20-PAGE_SHIFT))) >> (PAGE_SHIFT-11); limit = max(limit, 128UL); sysctl_tcp_mem[0] = limit / 4 * 3; sysctl_tcp_mem[1] = limit; -- cgit v0.10.2 From db43a282d3ec92ea45109c5551fff3dcc5afef02 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 27 Jun 2008 17:27:21 -0700 Subject: tcp: fix for splice receive when used with software LRO If an skb has nr_frags set to zero but its frag_list is not empty (as it can happen if software LRO is enabled), and a previous tcp_read_sock has consumed the linear part of the skb, then __skb_splice_bits: (a) incorrectly reports an error and (b) forgets to update the offset to account for the linear part Any of the two problems will cause the subsequent __skb_splice_bits call (the one that handles the frag_list skbs) to either skip data, or, if the unadjusted offset is greater then the size of the next skb in the frag_list, make tcp_splice_read loop forever. Signed-off-by: Octavian Purdila Signed-off-by: David S. Miller diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 1e556d3..3666216 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1292,12 +1292,14 @@ static int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset, { unsigned int nr_pages = spd->nr_pages; unsigned int poff, plen, len, toff, tlen; - int headlen, seg; + int headlen, seg, error = 0; toff = *offset; tlen = *total_len; - if (!tlen) + if (!tlen) { + error = 1; goto err; + } /* * if the offset is greater than the linear part, go directly to @@ -1339,7 +1341,8 @@ static int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset, * just jump directly to update and return, no point * in going over fragments when the output is full. */ - if (spd_fill_page(spd, virt_to_page(p), plen, poff, skb)) + error = spd_fill_page(spd, virt_to_page(p), plen, poff, skb); + if (error) goto done; tlen -= plen; @@ -1369,7 +1372,8 @@ map_frag: if (!plen) break; - if (spd_fill_page(spd, f->page, plen, poff, skb)) + error = spd_fill_page(spd, f->page, plen, poff, skb); + if (error) break; tlen -= plen; @@ -1382,7 +1386,10 @@ done: return 0; } err: - return 1; + /* update the offset to reflect the linear part skip, if any */ + if (!error) + *offset = toff; + return error; } /* -- cgit v0.10.2 From ec0d215f9420564fc8286dcf93d2d068bb53a07e Mon Sep 17 00:00:00 2001 From: Rainer Weikusat Date: Fri, 27 Jun 2008 19:34:18 -0700 Subject: af_unix: fix 'poll for write'/connected DGRAM sockets For n:1 'datagram connections' (eg /dev/log), the unix_dgram_sendmsg routine implements a form of receiver-imposed flow control by comparing the length of the receive queue of the 'peer socket' with the max_ack_backlog value stored in the corresponding sock structure, either blocking the thread which caused the send-routine to be called or returning EAGAIN. This routine is used by both SOCK_DGRAM and SOCK_SEQPACKET sockets. The poll-implementation for these socket types is datagram_poll from core/datagram.c. A socket is deemed to be writeable by this routine when the memory presently consumed by datagrams owned by it is less than the configured socket send buffer size. This is always wrong for PF_UNIX non-stream sockets connected to server sockets dealing with (potentially) multiple clients if the abovementioned receive queue is currently considered to be full. 'poll' will then return, indicating that the socket is writeable, but a subsequent write result in EAGAIN, effectively causing an (usual) application to 'poll for writeability by repeated send request with O_NONBLOCK set' until it has consumed its time quantum. The change below uses a suitably modified variant of the datagram_poll routines for both type of PF_UNIX sockets, which tests if the recv-queue of the peer a socket is connected to is presently considered to be 'full' as part of the 'is this socket writeable'-checking code. The socket being polled is additionally put onto the peer_wait wait queue associated with its peer, because the unix_dgram_recvmsg routine does a wake up on this queue after a datagram was received and the 'other wakeup call' is done implicitly as part of skb destruction, meaning, a process blocked in poll because of a full peer receive queue could otherwise sleep forever if no datagram owned by its socket was already sitting on this queue. Among this change is a small (inline) helper routine named 'unix_recvq_full', which consolidates the actual testing code (in three different places) into a single location. Signed-off-by: Rainer Weikusat Signed-off-by: David S. Miller diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 657835f..783317d 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -487,8 +487,8 @@ static int unix_socketpair(struct socket *, struct socket *); static int unix_accept(struct socket *, struct socket *, int); static int unix_getname(struct socket *, struct sockaddr *, int *, int); static unsigned int unix_poll(struct file *, struct socket *, poll_table *); -static unsigned int unix_datagram_poll(struct file *, struct socket *, - poll_table *); +static unsigned int unix_dgram_poll(struct file *, struct socket *, + poll_table *); static int unix_ioctl(struct socket *, unsigned int, unsigned long); static int unix_shutdown(struct socket *, int); static int unix_stream_sendmsg(struct kiocb *, struct socket *, @@ -534,7 +534,7 @@ static const struct proto_ops unix_dgram_ops = { .socketpair = unix_socketpair, .accept = sock_no_accept, .getname = unix_getname, - .poll = unix_datagram_poll, + .poll = unix_dgram_poll, .ioctl = unix_ioctl, .listen = sock_no_listen, .shutdown = unix_shutdown, @@ -555,7 +555,7 @@ static const struct proto_ops unix_seqpacket_ops = { .socketpair = unix_socketpair, .accept = unix_accept, .getname = unix_getname, - .poll = unix_datagram_poll, + .poll = unix_dgram_poll, .ioctl = unix_ioctl, .listen = unix_listen, .shutdown = unix_shutdown, @@ -1994,29 +1994,13 @@ static unsigned int unix_poll(struct file * file, struct socket *sock, poll_tabl return mask; } -static unsigned int unix_datagram_poll(struct file *file, struct socket *sock, - poll_table *wait) +static unsigned int unix_dgram_poll(struct file *file, struct socket *sock, + poll_table *wait) { - struct sock *sk = sock->sk, *peer; - unsigned int mask; + struct sock *sk = sock->sk, *other; + unsigned int mask, writable; poll_wait(file, sk->sk_sleep, wait); - - peer = unix_peer_get(sk); - if (peer) { - if (peer != sk) { - /* - * Writability of a connected socket additionally - * depends on the state of the receive queue of the - * peer. - */ - poll_wait(file, &unix_sk(peer)->peer_wait, wait); - } else { - sock_put(peer); - peer = NULL; - } - } - mask = 0; /* exceptional events? */ @@ -2042,14 +2026,26 @@ static unsigned int unix_datagram_poll(struct file *file, struct socket *sock, } /* writable? */ - if (unix_writable(sk) && !(peer && unix_recvq_full(peer))) + writable = unix_writable(sk); + if (writable) { + other = unix_peer_get(sk); + if (other) { + if (unix_peer(other) != sk) { + poll_wait(file, &unix_sk(other)->peer_wait, + wait); + if (unix_recvq_full(other)) + writable = 0; + } + + sock_put(other); + } + } + + if (writable) mask |= POLLOUT | POLLWRNORM | POLLWRBAND; else set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); - if (peer) - sock_put(peer); - return mask; } -- cgit v0.10.2 From 5dbaec5dc6a4895db8bf9765a867418481ed7311 Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Fri, 27 Jun 2008 19:35:16 -0700 Subject: netdevice: Fix typo of dev_unicast_add() comment Signed-off-by: Wang Chen Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index c421a1f..56b4657 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2973,7 +2973,7 @@ EXPORT_SYMBOL(dev_unicast_delete); /** * dev_unicast_add - add a secondary unicast address * @dev: device - * @addr: address to delete + * @addr: address to add * @alen: length of @addr * * Add a secondary unicast address to the device or increase -- cgit v0.10.2 From 01e123d79a23000f85c4cfb12a957908c0b2c3d8 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 27 Jun 2008 19:51:35 -0700 Subject: pkt_sched: ERR_PTR() ususally encodes an negative errno, not positive. Note, in the following patch, 'err' is initialized as: int err = -ENOBUFS; Signed-off-by: WANG Cong Signed-off-by: David S. Miller diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index d355e5e..13afa72 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -468,7 +468,7 @@ struct Qdisc *qdisc_alloc(struct net_device *dev, struct Qdisc_ops *ops) return sch; errout: - return ERR_PTR(-err); + return ERR_PTR(err); } struct Qdisc * qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops, -- cgit v0.10.2 From ede16af4cdbd21fa15d4178beb7c6fcbcccd07e9 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 27 Jun 2008 19:54:05 -0700 Subject: pkt_sched: Remove CONFIG_NET_SCH_RR Commit d62733c8e437fdb58325617c4b3331769ba82d70 ([SCHED]: Qdisc changes and sch_rr added for multiqueue) added a NET_SCH_RR option that was unused since the code went unconditionally into sch_prio. Reported-by: Robert P. J. Day Signed-off-by: Adrian Bunk Signed-off-by: David S. Miller diff --git a/net/sched/Kconfig b/net/sched/Kconfig index 82adfe6..9437b27 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -106,17 +106,6 @@ config NET_SCH_PRIO To compile this code as a module, choose M here: the module will be called sch_prio. -config NET_SCH_RR - tristate "Multi Band Round Robin Queuing (RR)" - select NET_SCH_PRIO - ---help--- - Say Y here if you want to use an n-band round robin packet - scheduler. - - The module uses sch_prio for its framework and is aliased as - sch_rr, so it will load sch_prio, although it is referred - to using sch_rr. - config NET_SCH_RED tristate "Random Early Detection (RED)" ---help--- -- cgit v0.10.2 From c88e6f51c2154c7606f7e281bcca2d1a2c89d7b2 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 27 Jun 2008 19:54:54 -0700 Subject: include/linux/netdevice.h: don't export MAX_HEADER to userspace Due to the CONFIG_'s the value is anyway not correct in userspace. Signed-off-by: Adrian Bunk Signed-off-by: David S. Miller diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f27fd20..25f8710 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -88,6 +88,8 @@ struct wireless_dev; #define NETDEV_TX_BUSY 1 /* driver tx path was busy*/ #define NETDEV_TX_LOCKED -1 /* driver tx lock was already taken */ +#ifdef __KERNEL__ + /* * Compute the worst case header length according to the protocols * used. @@ -114,6 +116,8 @@ struct wireless_dev; #define MAX_HEADER (LL_MAX_HEADER + 48) #endif +#endif /* __KERNEL__ */ + struct net_device_subqueue { /* Give a control state for each queue. This struct may contain -- cgit v0.10.2 From 7be87351a1f6430426e88b4fcde353ab3330caff Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 27 Jun 2008 20:00:19 -0700 Subject: tcp: /proc/net/tcp rto,ato values not scaled properly (v2) I found another case where we are sending information to userspace in the wrong HZ scale. This should have been fixed back in 2.5 :-( This means an ABI change but as it stands there is no way for an application like ss to get the right value. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 12695be..ffe869a 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2291,7 +2291,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len) } seq_printf(f, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX " - "%08X %5d %8d %lu %d %p %u %u %u %u %d%n", + "%08X %5d %8d %lu %d %p %lu %lu %u %u %d%n", i, src, srcp, dest, destp, sk->sk_state, tp->write_seq - tp->snd_una, sk->sk_state == TCP_LISTEN ? sk->sk_ack_backlog : @@ -2303,8 +2303,8 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len) icsk->icsk_probes_out, sock_i_ino(sk), atomic_read(&sk->sk_refcnt), sk, - icsk->icsk_rto, - icsk->icsk_ack.ato, + jiffies_to_clock_t(icsk->icsk_rto), + jiffies_to_clock_t(icsk->icsk_ack.ato), (icsk->icsk_ack.quick << 1) | icsk->icsk_ack.pingpong, tp->snd_cwnd, tp->snd_ssthresh >= 0xFFFF ? -1 : tp->snd_ssthresh, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index cb46749..40ea9c3 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -2036,7 +2036,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i) seq_printf(seq, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X " - "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %u %u %u %u %d\n", + "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %lu %lu %u %u %d\n", i, src->s6_addr32[0], src->s6_addr32[1], src->s6_addr32[2], src->s6_addr32[3], srcp, @@ -2052,8 +2052,8 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i) icsk->icsk_probes_out, sock_i_ino(sp), atomic_read(&sp->sk_refcnt), sp, - icsk->icsk_rto, - icsk->icsk_ack.ato, + jiffies_to_clock_t(icsk->icsk_rto), + jiffies_to_clock_t(icsk->icsk_ack.ato), (icsk->icsk_ack.quick << 1 ) | icsk->icsk_ack.pingpong, tp->snd_cwnd, tp->snd_ssthresh>=0xFFFF?-1:tp->snd_ssthresh ); -- cgit v0.10.2 From 10b595aff138961b520bfed51d664fd99980f6e9 Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Fri, 27 Jun 2008 20:02:14 -0700 Subject: netlink: Fix some doc comments in net/netlink/attr.c Fix some doc comments to match function and attribute names in net/netlink/attr.c. Signed-off-by: Julius Volz Signed-off-by: David S. Miller diff --git a/net/netlink/attr.c b/net/netlink/attr.c index 47bbf45..2d106cf 100644 --- a/net/netlink/attr.c +++ b/net/netlink/attr.c @@ -132,6 +132,7 @@ errout: * @maxtype: maximum attribute type to be expected * @head: head of attribute stream * @len: length of attribute stream + * @policy: validation policy * * Parses a stream of attributes and stores a pointer to each attribute in * the tb array accessable via the attribute type. Attributes with a type @@ -194,7 +195,7 @@ struct nlattr *nla_find(struct nlattr *head, int len, int attrtype) /** * nla_strlcpy - Copy string attribute payload into a sized buffer * @dst: where to copy the string to - * @src: attribute to copy the string from + * @nla: attribute to copy the string from * @dstsize: size of destination buffer * * Copies at most dstsize - 1 bytes into the destination buffer. @@ -340,9 +341,9 @@ struct nlattr *nla_reserve(struct sk_buff *skb, int attrtype, int attrlen) } /** - * nla_reserve - reserve room for attribute without header + * nla_reserve_nohdr - reserve room for attribute without header * @skb: socket buffer to reserve room on - * @len: length of attribute payload + * @attrlen: length of attribute payload * * Reserves room for attribute payload without a header. * -- cgit v0.10.2 From a0a61a604c60c14accc3962ecfeee9acc7a3c08a Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Fri, 27 Jun 2008 20:03:24 -0700 Subject: CONNECTOR: add a proc entry to list connectors I got a problem when I wanted to check if the kernel supports process event connector, and It seems there's no way to do this check. At best I can check if the kernel supports connector or not, by looking into /proc/net/netlink, or maybe checking the return value of bind() to see if it's ENOENT. So it would be useful to add /proc/net/connector to list all supported connectors: # cat /proc/net/connector Name ID connector 4294967295:4294967295 cn_proc 1:1 w1 3:1 Changelog: - fix memory leak: s/seq_release/single_release - use spin_lock_bh instead of spin_lock_irqsave Signed-off-by: Li Zefan Acked-by: Evgeniy Polyakov Signed-off-by: David S. Miller diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index 85e2ba7..bf48300 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include @@ -403,6 +405,40 @@ static void cn_callback(void *data) mutex_unlock(¬ify_lock); } +static int cn_proc_show(struct seq_file *m, void *v) +{ + struct cn_queue_dev *dev = cdev.cbdev; + struct cn_callback_entry *cbq; + + seq_printf(m, "Name ID\n"); + + spin_lock_bh(&dev->queue_lock); + + list_for_each_entry(cbq, &dev->queue_list, callback_entry) { + seq_printf(m, "%-15s %u:%u\n", + cbq->id.name, + cbq->id.id.idx, + cbq->id.id.val); + } + + spin_unlock_bh(&dev->queue_lock); + + return 0; +} + +static int cn_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cn_proc_show, NULL); +} + +static const struct file_operations cn_file_ops = { + .owner = THIS_MODULE, + .open = cn_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + static int __devinit cn_init(void) { struct cn_dev *dev = &cdev; @@ -434,6 +470,8 @@ static int __devinit cn_init(void) return -EINVAL; } + proc_net_fops_create(&init_net, "connector", S_IRUGO, &cn_file_ops); + return 0; } @@ -443,6 +481,8 @@ static void __devexit cn_fini(void) cn_already_initialized = 0; + proc_net_remove(&init_net, "connector"); + cn_del_callback(&dev->id); cn_queue_free_dev(dev->cbdev); netlink_kernel_release(dev->nls); -- cgit v0.10.2 From 9a375803feaadb6c34e0807bd9325885dcca5c00 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 27 Jun 2008 20:06:08 -0700 Subject: inet fragments: fix race between inet_frag_find and inet_frag_secret_rebuild The problem is that while we work w/o the inet_frags.lock even read-locked the secret rebuild timer may occur (on another CPU, since BHs are still disabled in the inet_frag_find) and change the rnd seed for ipv4/6 fragments. It was caused by my patch fd9e63544cac30a34c951f0ec958038f0529e244 ([INET]: Omit double hash calculations in xxx_frag_intern) late in the 2.6.24 kernel, so this should probably be queued to -stable. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 4ed429b..0546a0b 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -192,14 +192,21 @@ EXPORT_SYMBOL(inet_frag_evictor); static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf, struct inet_frag_queue *qp_in, struct inet_frags *f, - unsigned int hash, void *arg) + void *arg) { struct inet_frag_queue *qp; #ifdef CONFIG_SMP struct hlist_node *n; #endif + unsigned int hash; write_lock(&f->lock); + /* + * While we stayed w/o the lock other CPU could update + * the rnd seed, so we need to re-calculate the hash + * chain. Fortunatelly the qp_in can be used to get one. + */ + hash = f->hashfn(qp_in); #ifdef CONFIG_SMP /* With SMP race we have to recheck hash table, because * such entry could be created on other cpu, while we @@ -247,7 +254,7 @@ static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf, } static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf, - struct inet_frags *f, void *arg, unsigned int hash) + struct inet_frags *f, void *arg) { struct inet_frag_queue *q; @@ -255,7 +262,7 @@ static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf, if (q == NULL) return NULL; - return inet_frag_intern(nf, q, f, hash, arg); + return inet_frag_intern(nf, q, f, arg); } struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, @@ -264,7 +271,6 @@ struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, struct inet_frag_queue *q; struct hlist_node *n; - read_lock(&f->lock); hlist_for_each_entry(q, n, &f->hash[hash], list) { if (q->net == nf && f->match(q, key)) { atomic_inc(&q->refcnt); @@ -274,6 +280,6 @@ struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, } read_unlock(&f->lock); - return inet_frag_create(nf, f, key, hash); + return inet_frag_create(nf, f, key); } EXPORT_SYMBOL(inet_frag_find); diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index cd6ce6a..37221f6 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -229,6 +229,8 @@ static inline struct ipq *ip_find(struct net *net, struct iphdr *iph, u32 user) arg.iph = iph; arg.user = user; + + read_lock(&ip4_frags.lock); hash = ipqhashfn(iph->id, iph->saddr, iph->daddr, iph->protocol); q = inet_frag_find(&net->ipv4.frags, &ip4_frags, &arg, hash); diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index e65e26e..cf20bc4 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -207,9 +207,10 @@ fq_find(__be32 id, struct in6_addr *src, struct in6_addr *dst) arg.id = id; arg.src = src; arg.dst = dst; + + read_lock_bh(&nf_frags.lock); hash = ip6qhashfn(id, src, dst); - local_bh_disable(); q = inet_frag_find(&nf_init_frags, &nf_frags, &arg, hash); local_bh_enable(); if (q == NULL) diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 798cabc..a60d7d1 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -247,6 +247,8 @@ fq_find(struct net *net, __be32 id, struct in6_addr *src, struct in6_addr *dst, arg.id = id; arg.src = src; arg.dst = dst; + + read_lock(&ip6_frags.lock); hash = ip6qhashfn(id, src, dst); q = inet_frag_find(&net->ipv6.frags, &ip6_frags, &arg, hash); -- cgit v0.10.2 From 251a4b320f2352598f84e4452ab538aa8064af52 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Fri, 27 Jun 2008 20:09:00 -0700 Subject: net/inet_lro: remove setting skb->ip_summed when not LRO-able When an SKB cannot be chained to a session, the current code attempts to "restore" its ip_summed field from lro_mgr->ip_summed. However, lro_mgr->ip_summed does not hold the original value; in fact, we'd better not touch skb->ip_summed since it is not modified by the code in the path leading to a failure to chain it. Also use a cleaer comment to the describe the ip_summed field of struct net_lro_mgr. Issue raised by Or Gerlitz Signed-off-by: Eli Cohen Signed-off-by: David S. Miller diff --git a/include/linux/inet_lro.h b/include/linux/inet_lro.h index 80335b7..c4335fa 100644 --- a/include/linux/inet_lro.h +++ b/include/linux/inet_lro.h @@ -84,7 +84,11 @@ struct net_lro_mgr { from received packets and eth protocol is still ETH_P_8021Q */ - u32 ip_summed; /* Set in non generated SKBs in page mode */ + /* + * Set for generated SKBs that are not added to + * the frag list in fragmented mode + */ + u32 ip_summed; u32 ip_summed_aggr; /* Set in aggregated SKBs: CHECKSUM_UNNECESSARY * or CHECKSUM_NONE */ diff --git a/net/ipv4/inet_lro.c b/net/ipv4/inet_lro.c index 4a4d49f..cfd034a 100644 --- a/net/ipv4/inet_lro.c +++ b/net/ipv4/inet_lro.c @@ -383,8 +383,7 @@ static int __lro_proc_skb(struct net_lro_mgr *lro_mgr, struct sk_buff *skb, out2: /* send aggregated SKBs to stack */ lro_flush(lro_mgr, lro_desc); -out: /* Original SKB has to be posted to stack */ - skb->ip_summed = lro_mgr->ip_summed; +out: return 1; } -- cgit v0.10.2 From 59d88c00cafe5192b058abf4f3ce17c2e27d1c09 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 27 Jun 2008 20:12:32 -0700 Subject: netlabel: Fix a problem when dumping the default IPv6 static labels There is a missing "!" in a conditional statement which is causing entries to be skipped when dumping the default IPv6 static label entries. This can be demonstrated by running the following: # netlabelctl unlbl add default address:::1 \ label:system_u:object_r:unlabeled_t:s0 # netlabelctl -p unlbl list ... you will notice that the entry for the IPv6 localhost address is not displayed but does exist (works correctly, causes collisions when attempting to add duplicate entries, etc.). Signed-off-by: Paul Moore Signed-off-by: David S. Miller diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index 0099da5b..52b2611 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -1534,7 +1534,7 @@ static int netlbl_unlabel_staticlistdef(struct sk_buff *skb, } } list_for_each_entry_rcu(addr6, &iface->addr6_list, list) { - if (addr6->valid || iter_addr6++ < skip_addr6) + if (!addr6->valid || iter_addr6++ < skip_addr6) continue; if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF, iface, -- cgit v0.10.2 From d420895efb259a78dda50f95289571faa6e10e41 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki Date: Fri, 27 Jun 2008 20:14:54 -0700 Subject: ipv6 route: Convert rt6_device_match() to use RT6_LOOKUP_F_xxx flags. The commit 77d16f450ae0452d7d4b009f78debb1294fb435c ("[IPV6] ROUTE: Unify RT6_F_xxx and RT6_SELECT_F_xxx flags") intended to pass various routing lookup hints around RT6_LOOKUP_F_xxx flags, but conversion was missing for rt6_device_match(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller diff --git a/net/ipv6/route.c b/net/ipv6/route.c index d1f3e19..7ff6870 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -240,7 +240,7 @@ static inline int rt6_need_strict(struct in6_addr *daddr) static inline struct rt6_info *rt6_device_match(struct net *net, struct rt6_info *rt, int oif, - int strict) + int flags) { struct rt6_info *local = NULL; struct rt6_info *sprt; @@ -253,7 +253,7 @@ static inline struct rt6_info *rt6_device_match(struct net *net, if (dev->flags & IFF_LOOPBACK) { if (sprt->rt6i_idev == NULL || sprt->rt6i_idev->dev->ifindex != oif) { - if (strict && oif) + if (flags & RT6_LOOKUP_F_IFACE && oif) continue; if (local && (!oif || local->rt6i_idev->dev->ifindex == oif)) @@ -266,7 +266,7 @@ static inline struct rt6_info *rt6_device_match(struct net *net, if (local) return local; - if (strict) + if (flags & RT6_LOOKUP_F_IFACE) return net->ipv6.ip6_null_entry; } return rt; -- cgit v0.10.2 From 79c537998d143b127c8c662a403c3356cb885f1c Mon Sep 17 00:00:00 2001 From: Dmitry Adamushko Date: Sun, 29 Jun 2008 00:16:56 +0200 Subject: sched: fix cpu hotplug the CPU hotplug problems (crashes under high-volume unplug+replug tests) seem to be related to migrate_dead_tasks(). Firstly I added traces to see all tasks being migrated with migrate_live_tasks() and migrate_dead_tasks(). On my setup the problem pops up (the one with "se == NULL" in the loop of pick_next_task_fair()) shortly after the traces indicate that some has been migrated with migrate_dead_tasks()). btw., I can reproduce it much faster now with just a plain cpu down/up loop. [disclaimer] Well, unless I'm really missing something important in this late hour [/desclaimer] pick_next_task() is not something appropriate for migrate_dead_tasks() :-) the following change seems to eliminate the problem on my setup (although, I kept it running only for a few minutes to get a few messages indicating migrate_dead_tasks() does move tasks and the system is still ok) Signed-off-by: Ingo Molnar diff --git a/kernel/sched.c b/kernel/sched.c index 3aaa5c8..a66e856 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -5887,6 +5887,7 @@ static void migrate_dead_tasks(unsigned int dead_cpu) next = pick_next_task(rq, rq->curr); if (!next) break; + next->sched_class->put_prev_task(rq, next); migrate_dead(dead_cpu, next); } -- cgit v0.10.2 From f72e9df0e04c0ea7b5c97e6db5b114234bf3c36f Mon Sep 17 00:00:00 2001 From: Eduard - Gabriel Munteanu Date: Thu, 26 Jun 2008 10:54:34 +0300 Subject: Fix and clean top .gitignore Removed vmlinux* rule because it matches too many useful files, replacing it with rules matching filetype by filename (e.g. *.gz). Also unignored .mailmap from the top directory. Added a comment telling the user how to check for tracked files being ignored. Signed-off-by: Eduard - Gabriel Munteanu Signed-off-by: Linus Torvalds diff --git a/.gitignore b/.gitignore index 9bb1cb6..869e1a3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,10 @@ # subdirectories here. Add them in the ".gitignore" file # in that subdirectory instead. # +# NOTE! Please use 'git-ls-files -i --exclude-standard' +# command after changing this file, to see if there are +# any tracked files which get ignored after the change. +# # Normal rules # .* @@ -18,19 +22,21 @@ *.lst *.symtypes *.order +*.elf +*.bin +*.gz # # Top-level generic files # tags TAGS -vmlinux* -!vmlinux.lds.S -!vmlinux.lds.h +vmlinux System.map Module.markers Module.symvers !.gitignore +!.mailmap # # Generated include files -- cgit v0.10.2 From fcb43042ef55d2f46b0efa5d7746967cef38f056 Mon Sep 17 00:00:00 2001 From: "Zhang, Yanmin" Date: Tue, 24 Jun 2008 16:06:23 +0800 Subject: x86: fix cpu hotplug crash Vegard Nossum reported crashes during cpu hotplug tests: http://marc.info/?l=linux-kernel&m=121413950227884&w=4 In function _cpu_up, the panic happens when calling __raw_notifier_call_chain at the second time. Kernel doesn't panic when calling it at the first time. If just say because of nr_cpu_ids, that's not right. By checking the source code, I found that function do_boot_cpu is the culprit. Consider below call chain: _cpu_up=>__cpu_up=>smp_ops.cpu_up=>native_cpu_up=>do_boot_cpu. So do_boot_cpu is called in the end. In do_boot_cpu, if boot_error==true, cpu_clear(cpu, cpu_possible_map) is executed. So later on, when _cpu_up calls __raw_notifier_call_chain at the second time to report CPU_UP_CANCELED, because this cpu is already cleared from cpu_possible_map, get_cpu_sysdev returns NULL. Many resources are related to cpu_possible_map, so it's better not to change it. Below patch against 2.6.26-rc7 fixes it by removing the bit clearing in cpu_possible_map. Signed-off-by: Zhang Yanmin Tested-by: Vegard Nossum Acked-by: Rusty Russell Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 56078d6..3e1cece 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -996,7 +996,6 @@ do_rest: #endif cpu_clear(cpu, cpu_callout_map); /* was set by do_boot_cpu() */ cpu_clear(cpu, cpu_initialized); /* was set by cpu_init() */ - cpu_clear(cpu, cpu_possible_map); cpu_clear(cpu, cpu_present_map); per_cpu(x86_cpu_to_apicid, cpu) = BAD_APICID; } -- cgit v0.10.2 From 11dbc963a8f6128595d0f6ecf138dc369e144997 Mon Sep 17 00:00:00 2001 From: TAKADA Yoshihito Date: Mon, 30 Jun 2008 13:44:45 +0900 Subject: ptrace GET/SET FPXREGS broken When I update kernel 2.6.25 from 2.6.24, gdb does not work. On 2.6.25, ptrace(PTRACE_GETFPXREGS, ...) returns ENODEV. But 2.6.24 kernel's ptrace() returns EIO. It is issue of compatibility. I attached test program as pt.c and patch for fix it. #include #include #include #include #include #include #include struct user_fxsr_struct { unsigned short cwd; unsigned short swd; unsigned short twd; unsigned short fop; long fip; long fcs; long foo; long fos; long mxcsr; long reserved; long st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ long xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */ long padding[56]; }; int main(void) { pid_t pid; pid = fork(); switch(pid){ case -1:/* error */ break; case 0:/* child */ child(); break; default: parent(pid); break; } return 0; } int child(void) { ptrace(PTRACE_TRACEME); kill(getpid(), SIGSTOP); sleep(10); return 0; } int parent(pid_t pid) { int ret; struct user_fxsr_struct fpxregs; ret = ptrace(PTRACE_GETFPXREGS, pid, 0, &fpxregs); if(ret < 0){ printf("%d: %s.\n", errno, strerror(errno)); } kill(pid, SIGCONT); wait(pid); return 0; } /* in the kerel, at kernel/i387.c get_fpxregs() */ Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index eb9ddd8..95e80e5 100644 --- a/arch/x86/kernel/i387.c +++ b/arch/x86/kernel/i387.c @@ -162,7 +162,7 @@ int xfpregs_get(struct task_struct *target, const struct user_regset *regset, int ret; if (!cpu_has_fxsr) - return -ENODEV; + return -EIO; ret = init_fpu(target); if (ret) @@ -179,7 +179,7 @@ int xfpregs_set(struct task_struct *target, const struct user_regset *regset, int ret; if (!cpu_has_fxsr) - return -ENODEV; + return -EIO; ret = init_fpu(target); if (ret) -- cgit v0.10.2 From 4bbff7e408a54cce88d26191191e8bcda2a60d55 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Thu, 26 Jun 2008 09:13:48 -0400 Subject: Input: add KEY_MEDIA_REPEAT definition This patch adds the Repeat key to the input layer. The usage in the HUT is 0xBC (listed under "15.7 Transport Controls"). Signed-off-by: Dmitry Torokhov diff --git a/include/linux/input.h b/include/linux/input.h index e075c4b..d150c57 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -534,8 +534,8 @@ struct input_absinfo { #define KEY_FRAMEBACK 0x1b4 /* Consumer - transport controls */ #define KEY_FRAMEFORWARD 0x1b5 - #define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */ +#define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */ #define KEY_DEL_EOL 0x1c0 #define KEY_DEL_EOS 0x1c1 -- cgit v0.10.2 From 656acd2bbc4ce7f224de499ee255698701396c48 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 26 Jun 2008 11:30:02 -0400 Subject: Input: fix locking in force-feedback core The newly added event_lock spinlock in the input core disallows sleeping and therefore using mutexes in event handlers. Convert force-feedback core to rely on event_lock instead of mutex to protect slots allocated for fore-feedback effects. The original mutex is still used to serialize uploading and erasing of effects. Reported-by: Anssi Hannula Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index eebc724..4c01464 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -166,8 +166,10 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, if (ret) goto out; + spin_lock_irq(&dev->event_lock); ff->effects[id] = *effect; ff->effect_owners[id] = file; + spin_unlock_irq(&dev->event_lock); out: mutex_unlock(&ff->mutex); @@ -189,16 +191,22 @@ static int erase_effect(struct input_dev *dev, int effect_id, if (error) return error; + spin_lock_irq(&dev->event_lock); ff->playback(dev, effect_id, 0); + ff->effect_owners[effect_id] = NULL; + spin_unlock_irq(&dev->event_lock); if (ff->erase) { error = ff->erase(dev, effect_id); - if (error) + if (error) { + spin_lock_irq(&dev->event_lock); + ff->effect_owners[effect_id] = file; + spin_unlock_irq(&dev->event_lock); + return error; + } } - ff->effect_owners[effect_id] = NULL; - return 0; } @@ -263,8 +271,6 @@ int input_ff_event(struct input_dev *dev, unsigned int type, if (type != EV_FF) return 0; - mutex_lock(&ff->mutex); - switch (code) { case FF_GAIN: if (!test_bit(FF_GAIN, dev->ffbit) || value > 0xffff) @@ -286,7 +292,6 @@ int input_ff_event(struct input_dev *dev, unsigned int type, break; } - mutex_unlock(&ff->mutex); return 0; } EXPORT_SYMBOL_GPL(input_ff_event); -- cgit v0.10.2 From 08383ef29faa7fa247962e6b8662c8683e34da01 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Fri, 27 Jun 2008 15:15:12 +0100 Subject: [ARM] 5131/1: Annotate platform_secondary_init with trace_hardirqs_off This patch annotates the platform_secondary_init function in arch/arm/mach-realview/platsmp.c with trace_hardirqs_off to avoid a warning when LOCKDEP and TRACE_IRQFLAGS are enabled. Signed-off-by: Catalin Marinas Signed-off-by: Russell King diff --git a/arch/arm/mach-realview/platsmp.c b/arch/arm/mach-realview/platsmp.c index 3e57428..8e813ed 100644 --- a/arch/arm/mach-realview/platsmp.c +++ b/arch/arm/mach-realview/platsmp.c @@ -74,6 +74,8 @@ static DEFINE_SPINLOCK(boot_lock); void __cpuinit platform_secondary_init(unsigned int cpu) { + trace_hardirqs_off(); + /* * the primary core may have used a "cross call" soft interrupt * to get this processor out of WFI in the BootMonitor - make -- cgit v0.10.2 From a529b59060862b36a4dae968534e090c6c77272e Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 30 Jun 2008 20:13:31 +0200 Subject: Revert "BAST: Remove old IDE driver" This reverts commit ac1623625c5818bbdf5c68973098ba386ba7a004. It was premature to remove it now, we will do it post-2.6.26. Thanks to Russell King for noticing the issue. Signed-off-by: Bartlomiej Zolnierkiewicz diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 8e07de2..1607536 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -823,6 +823,13 @@ config BLK_DEV_IDE_RAPIDE Say Y here if you want to support the Yellowstone RapIDE controller manufactured for use with Acorn computers. +config BLK_DEV_IDE_BAST + tristate "Simtec BAST / Thorcom VR1000 IDE support" + depends on ARM && (ARCH_BAST || MACH_VR1000) + help + Say Y here if you want to support the onboard IDE channels on the + Simtec BAST or the Thorcom VR1000 + config IDE_H8300 tristate "H8300 IDE support" depends on H8300 diff --git a/drivers/ide/arm/Makefile b/drivers/ide/arm/Makefile index 5bc2605..936e7b0 100644 --- a/drivers/ide/arm/Makefile +++ b/drivers/ide/arm/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o +obj-$(CONFIG_BLK_DEV_IDE_BAST) += bast-ide.o obj-$(CONFIG_BLK_DEV_PALMCHIP_BK3710) += palm_bk3710.o ifeq ($(CONFIG_IDE_ARM), m) diff --git a/drivers/ide/arm/bast-ide.c b/drivers/ide/arm/bast-ide.c new file mode 100644 index 0000000..8e8c281 --- /dev/null +++ b/drivers/ide/arm/bast-ide.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2003-2004 Simtec Electronics + * Ben Dooks + * + * 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 + +#define DRV_NAME "bast-ide" + +static int __init bastide_register(unsigned int base, unsigned int aux, int irq) +{ + ide_hwif_t *hwif; + hw_regs_t hw; + int i; + u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + + memset(&hw, 0, sizeof(hw)); + + base += BAST_IDE_CS; + aux += BAST_IDE_CS; + + for (i = 0; i <= 7; i++) { + hw.io_ports_array[i] = (unsigned long)base; + base += 0x20; + } + + hw.io_ports.ctl_addr = aux + (6 * 0x20); + hw.irq = irq; + hw.chipset = ide_generic; + + hwif = ide_find_port(); + if (hwif == NULL) + goto out; + + i = hwif->index; + + ide_init_port_data(hwif, i); + ide_init_port_hw(hwif, &hw); + hwif->port_ops = NULL; + + idx[0] = i; + + ide_device_add(idx, NULL); +out: + return 0; +} + +static int __init bastide_init(void) +{ + unsigned long base = BAST_VA_IDEPRI + BAST_IDE_CS; + + /* we can treat the VR1000 and the BAST the same */ + + if (!(machine_is_bast() || machine_is_vr1000())) + return 0; + + printk("BAST: IDE driver, (c) 2003-2004 Simtec Electronics\n"); + + if (!request_mem_region(base, 0x400000, DRV_NAME)) { + printk(KERN_ERR "%s: resources busy\n", DRV_NAME); + return -EBUSY; + } + + bastide_register(BAST_VA_IDEPRI, BAST_VA_IDEPRIAUX, IRQ_IDE0); + bastide_register(BAST_VA_IDESEC, BAST_VA_IDESECAUX, IRQ_IDE1); + + return 0; +} + +module_init(bastide_init); + +MODULE_AUTHOR("Ben Dooks "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Simtec BAST / Thorcom VR1000 IDE driver"); -- cgit v0.10.2 From 4283e1babe167e0ba856bb5d039465358e90785c Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 30 Jun 2008 20:14:45 +0200 Subject: ide: fix /proc/ide/ide?/mate reporting Now that we support warm-plug mate port will be registered even if there are no devices attached to it. Signed-off-by: Bartlomiej Zolnierkiewicz diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index 55ec7f7..8af88bf 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -76,7 +76,7 @@ static int proc_ide_read_mate ide_hwif_t *hwif = (ide_hwif_t *) data; int len; - if (hwif && hwif->mate && hwif->mate->present) + if (hwif && hwif->mate) len = sprintf(page, "%s\n", hwif->mate->name); else len = sprintf(page, "(none)\n"); -- cgit v0.10.2 From 84ebe1cdae56707b9aa1b40ae5aa7d817ba745f5 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Mon, 30 Jun 2008 12:41:30 -0700 Subject: netfilter: nf_conntrack_tcp: fixing to check the lower bound of valid ACK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lost connections was reported by Thomas Bätzler (running 2.6.25 kernel) on the netfilter mailing list (see the thread "Weird nat/conntrack Problem with PASV FTP upload"). He provided tcpdump recordings which helped to find a long lingering bug in conntrack. In TCP connection tracking, checking the lower bound of valid ACK could lead to mark valid packets as INVALID because: - We have got a "higher or equal" inequality, but the test checked the "higher" condition only; fixed. - If the packet contains a SACK option, it could occur that the ACK value was before the left edge of our (S)ACK "window": if a previous packet from the other party intersected the right edge of the window of the receiver, we could move forward the window parameters beyond accepting a valid ack. Therefore in this patch we check the rightmost SACK edge instead of the ACK value in the lower bound of valid (S)ACK test. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index ba94004..271cd01 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -331,12 +331,13 @@ static unsigned int get_conntrack_index(const struct tcphdr *tcph) I. Upper bound for valid data: seq <= sender.td_maxend II. Lower bound for valid data: seq + len >= sender.td_end - receiver.td_maxwin - III. Upper bound for valid ack: sack <= receiver.td_end - IV. Lower bound for valid ack: ack >= receiver.td_end - MAXACKWINDOW + III. Upper bound for valid (s)ack: sack <= receiver.td_end + IV. Lower bound for valid (s)ack: sack >= receiver.td_end - MAXACKWINDOW - where sack is the highest right edge of sack block found in the packet. + where sack is the highest right edge of sack block found in the packet + or ack in the case of packet without SACK option. - The upper bound limit for a valid ack is not ignored - + The upper bound limit for a valid (s)ack is not ignored - we doesn't have to deal with fragments. */ @@ -606,12 +607,12 @@ static bool tcp_in_window(const struct nf_conn *ct, before(seq, sender->td_maxend + 1), after(end, sender->td_end - receiver->td_maxwin - 1), before(sack, receiver->td_end + 1), - after(ack, receiver->td_end - MAXACKWINDOW(sender))); + after(sack, receiver->td_end - MAXACKWINDOW(sender) - 1)); if (before(seq, sender->td_maxend + 1) && after(end, sender->td_end - receiver->td_maxwin - 1) && before(sack, receiver->td_end + 1) && - after(ack, receiver->td_end - MAXACKWINDOW(sender))) { + after(sack, receiver->td_end - MAXACKWINDOW(sender) - 1)) { /* * Take into account window scaling (RFC 1323). */ -- cgit v0.10.2 From aebb6a849cfe7d89bcacaaecc20a480dfc1180e7 Mon Sep 17 00:00:00 2001 From: Joonwoo Park Date: Mon, 30 Jun 2008 12:42:23 -0700 Subject: textsearch: fix Boyer-Moore text search bug The current logic has a bug which cannot find matching pattern, if the pattern is matched from the first character of target string. for example: pattern=abc, string=abcdefg pattern=a, string=abcdefg Searching algorithm should return 0 for those things. Signed-off-by: Joonwoo Park Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller diff --git a/lib/ts_bm.c b/lib/ts_bm.c index d90822c..4a7fce7 100644 --- a/lib/ts_bm.c +++ b/lib/ts_bm.c @@ -63,7 +63,7 @@ static unsigned int bm_find(struct ts_config *conf, struct ts_state *state) struct ts_bm *bm = ts_config_priv(conf); unsigned int i, text_len, consumed = state->offset; const u8 *text; - int shift = bm->patlen, bs; + int shift = bm->patlen - 1, bs; for (;;) { text_len = conf->get_next_block(consumed, &text, conf, state); -- cgit v0.10.2 From 15ea0ebc5b7305cc75189cb6b7924d0db5278e0c Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Fri, 27 Jun 2008 16:19:52 -0400 Subject: hostap: don't report useless WDS frames by default DEBUG_EXTRA is reported to the kernel log by default, but DEBUG_EXTRA2 is not. Unrelated WDS frames pollute the log unnecessarily. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c index 4fd7380..47884c3 100644 --- a/drivers/net/wireless/hostap/hostap_80211_rx.c +++ b/drivers/net/wireless/hostap/hostap_80211_rx.c @@ -551,7 +551,7 @@ hostap_rx_frame_wds(local_info_t *local, struct ieee80211_hdr_4addr *hdr, hdr->addr1[2] != 0xff || hdr->addr1[3] != 0xff || hdr->addr1[4] != 0xff || hdr->addr1[5] != 0xff)) { /* RA (or BSSID) is not ours - drop */ - PDEBUG(DEBUG_EXTRA, "%s: received WDS frame with " + PDEBUG(DEBUG_EXTRA2, "%s: received WDS frame with " "not own or broadcast %s=%s\n", local->dev->name, fc & IEEE80211_FCTL_FROMDS ? "RA" : "BSSID", -- cgit v0.10.2 From 1bcca3c463e4930cef9986b05165bb0b3eb46f63 Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Fri, 27 Jun 2008 16:19:58 -0400 Subject: hostap: fix sparse warnings Rewrite AID calculation in handle_pspoll() to avoid truncating bits. Make hostap_80211_header_parse() static, don't export it. Avoid shadowing variables. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c index 47884c3..020f450 100644 --- a/drivers/net/wireless/hostap/hostap_80211_rx.c +++ b/drivers/net/wireless/hostap/hostap_80211_rx.c @@ -64,7 +64,7 @@ int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, int hdrlen, phdrlen, head_need, tail_need; u16 fc; int prism_header, ret; - struct ieee80211_hdr_4addr *hdr; + struct ieee80211_hdr_4addr *fhdr; iface = netdev_priv(dev); local = iface->local; @@ -83,8 +83,8 @@ int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, phdrlen = 0; } - hdr = (struct ieee80211_hdr_4addr *) skb->data; - fc = le16_to_cpu(hdr->frame_ctl); + fhdr = (struct ieee80211_hdr_4addr *) skb->data; + fc = le16_to_cpu(fhdr->frame_ctl); if (type == PRISM2_RX_MGMT && (fc & IEEE80211_FCTL_VERS)) { printk(KERN_DEBUG "%s: dropped management frame with header " diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c index 0acd958..ab981af 100644 --- a/drivers/net/wireless/hostap/hostap_ap.c +++ b/drivers/net/wireless/hostap/hostap_ap.c @@ -1930,7 +1930,7 @@ static void handle_pspoll(local_info_t *local, PDEBUG(DEBUG_PS, " PSPOLL and AID[15:14] not set\n"); return; } - aid &= ~BIT(15) & ~BIT(14); + aid &= ~(BIT(15) | BIT(14)); if (aid == 0 || aid > MAX_AID_TABLE_SIZE) { PDEBUG(DEBUG_PS, " invalid aid=%d\n", aid); return; diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index ed4317a1..80039a0 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c @@ -533,10 +533,10 @@ static void prism2_detach(struct pcmcia_device *link) do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) #define CFG_CHECK2(fn, retf) \ -do { int ret = (retf); \ -if (ret != 0) { \ - PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", ret); \ - cs_error(link, fn, ret); \ +do { int _ret = (retf); \ +if (_ret != 0) { \ + PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", _ret); \ + cs_error(link, fn, _ret); \ goto next_entry; \ } \ } while (0) diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index cdf90c4..936f52e 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -2835,7 +2835,7 @@ static void hostap_passive_scan(unsigned long data) { local_info_t *local = (local_info_t *) data; struct net_device *dev = local->dev; - u16 channel; + u16 chan; if (local->passive_scan_interval <= 0) return; @@ -2872,11 +2872,11 @@ static void hostap_passive_scan(unsigned long data) printk(KERN_DEBUG "%s: passive scan channel %d\n", dev->name, local->passive_scan_channel); - channel = local->passive_scan_channel; + chan = local->passive_scan_channel; local->passive_scan_state = PASSIVE_SCAN_WAIT; local->passive_scan_timer.expires = jiffies + HZ / 10; } else { - channel = local->channel; + chan = local->channel; local->passive_scan_state = PASSIVE_SCAN_LISTEN; local->passive_scan_timer.expires = jiffies + local->passive_scan_interval * HZ; @@ -2884,9 +2884,9 @@ static void hostap_passive_scan(unsigned long data) if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_TEST | (HFA384X_TEST_CHANGE_CHANNEL << 8), - channel, NULL, 0)) + chan, NULL, 0)) printk(KERN_ERR "%s: passive scan channel set %d " - "failed\n", dev->name, channel); + "failed\n", dev->name, chan); add_timer(&local->passive_scan_timer); } diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c index f7aec93..a38e85f 100644 --- a/drivers/net/wireless/hostap/hostap_main.c +++ b/drivers/net/wireless/hostap/hostap_main.c @@ -594,7 +594,8 @@ void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx) } -int hostap_80211_header_parse(const struct sk_buff *skb, unsigned char *haddr) +static int hostap_80211_header_parse(const struct sk_buff *skb, + unsigned char *haddr) { struct hostap_interface *iface = netdev_priv(skb->dev); local_info_t *local = iface->local; @@ -857,7 +858,6 @@ const struct header_ops hostap_80211_ops = { .rebuild = eth_rebuild_header, .cache = eth_header_cache, .cache_update = eth_header_cache_update, - .parse = hostap_80211_header_parse, }; EXPORT_SYMBOL(hostap_80211_ops); @@ -1150,7 +1150,6 @@ EXPORT_SYMBOL(hostap_set_roaming); EXPORT_SYMBOL(hostap_set_auth_algs); EXPORT_SYMBOL(hostap_dump_rx_header); EXPORT_SYMBOL(hostap_dump_tx_header); -EXPORT_SYMBOL(hostap_80211_header_parse); EXPORT_SYMBOL(hostap_80211_get_hdrlen); EXPORT_SYMBOL(hostap_get_stats); EXPORT_SYMBOL(hostap_setup_dev); -- cgit v0.10.2 From 23976efedd5ecb420b87455787c537eb4aed1981 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sat, 28 Jun 2008 02:50:13 +0300 Subject: mac80211: don't accept WEP keys other than WEP40 and WEP104 This patch makes mac80211 refuse a WEP key whose length is not WEP40 nor WEP104. Signed-off-by: Emmanuel Grumbach Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville diff --git a/include/net/mac80211.h b/include/net/mac80211.h index dae3f9e..bcd1623 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -595,6 +595,15 @@ enum ieee80211_key_alg { ALG_CCMP, }; +/** + * enum ieee80211_key_len - key length + * @WEP40: WEP 5 byte long key + * @WEP104: WEP 13 byte long key + */ +enum ieee80211_key_len { + LEN_WEP40 = 5, + LEN_WEP104 = 13, +}; /** * enum ieee80211_key_flags - key flags diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 6106cb7..e840421 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -95,6 +95,13 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, } } + if (alg == ALG_WEP && + key_len != LEN_WEP40 && key_len != LEN_WEP104) { + ieee80211_key_free(key); + err = -EINVAL; + goto out_unlock; + } + ieee80211_key_link(key, sdata, sta); if (set_tx_key || (!sta && !sdata->default_key && key)) -- cgit v0.10.2 From c0efd232929c2cd87238de2cccdaf4e845be5b0c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 30 Jun 2008 15:04:50 -0300 Subject: V4L/DVB (8145a): USB Video Class driver This driver supports video input devices compliant with the USB Video Class specification. This means lots of currently manufactured webcams, and probably most of the future ones. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/MAINTAINERS b/MAINTAINERS index 8f0ec46..e6c06fa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4314,6 +4314,14 @@ L: netdev@vger.kernel.org W: http://www.linux-usb.org/usbnet S: Maintained +USB VIDEO CLASS +P: Laurent Pinchart +M: laurent.pinchart@skynet.be +L: linx-uvc-devel@berlios.de +L: video4linux-list@redhat.com +W: http://linux-uvc.berlios.de +S: Maintained + USB W996[87]CF DRIVER P: Luca Risolia M: luca.risolia@studio.unibo.it diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 3b26fbd3..5ccb0ae 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -793,6 +793,14 @@ menuconfig V4L_USB_DRIVERS if V4L_USB_DRIVERS && USB +config USB_VIDEO_CLASS + tristate "USB Video Class (UVC)" + ---help--- + Support for the USB Video Class (UVC). Currently only video + input devices, such as webcams, are supported. + + For more information see: + source "drivers/media/video/pvrusb2/Kconfig" source "drivers/media/video/em28xx/Kconfig" diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index dff0d6a..ecbbfaa 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -136,6 +136,8 @@ obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o obj-$(CONFIG_VIDEO_AU0828) += au0828/ +obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/video/uvc/Makefile b/drivers/media/video/uvc/Makefile new file mode 100644 index 0000000..fb39bbf --- /dev/null +++ b/drivers/media/video/uvc/Makefile @@ -0,0 +1,3 @@ +uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \ + uvc_status.o uvc_isight.o +obj-$(CONFIG_USB_VIDEO_CLASS) := uvcvideo.o diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c new file mode 100644 index 0000000..f0ee46d --- /dev/null +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -0,0 +1,1256 @@ +/* + * uvc_ctrl.c -- USB Video Class driver - Controls + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uvcvideo.h" + +#define UVC_CTRL_NDATA 2 +#define UVC_CTRL_DATA_CURRENT 0 +#define UVC_CTRL_DATA_BACKUP 1 + +/* ------------------------------------------------------------------------ + * Control, formats, ... + */ + +static struct uvc_control_info uvc_ctrls[] = { + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_BRIGHTNESS_CONTROL, + .index = 0, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_CONTRAST_CONTROL, + .index = 1, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_HUE_CONTROL, + .index = 2, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_SATURATION_CONTROL, + .index = 3, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_SHARPNESS_CONTROL, + .index = 4, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_GAMMA_CONTROL, + .index = 5, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_BACKLIGHT_COMPENSATION_CONTROL, + .index = 8, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_GAIN_CONTROL, + .index = 9, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_POWER_LINE_FREQUENCY_CONTROL, + .index = 10, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_HUE_AUTO_CONTROL, + .index = 11, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_AE_MODE_CONTROL, + .index = 1, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_GET_RES + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_AE_PRIORITY_CONTROL, + .index = 2, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, + .index = 3, + .size = 4, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_FOCUS_ABSOLUTE_CONTROL, + .index = 5, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_FOCUS_AUTO_CONTROL, + .index = 17, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, + .index = 12, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL, + .index = 6, + .size = 2, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, + .index = 13, + .size = 1, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR + | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, + }, + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .index = 7, + .size = 4, + .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE + | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, + }, +}; + +static struct uvc_menu_info power_line_frequency_controls[] = { + { 0, "Disabled" }, + { 1, "50 Hz" }, + { 2, "60 Hz" }, +}; + +static struct uvc_menu_info exposure_auto_controls[] = { + { 1, "Manual Mode" }, + { 2, "Auto Mode" }, + { 4, "Shutter Priority Mode" }, + { 8, "Aperture Priority Mode" }, +}; + +static struct uvc_control_mapping uvc_ctrl_mappings[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .name = "Brightness", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_BRIGHTNESS_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_CONTRAST, + .name = "Contrast", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_CONTRAST_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_HUE, + .name = "Hue", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_HUE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_SATURATION, + .name = "Saturation", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_SATURATION_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_SHARPNESS, + .name = "Sharpness", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_SHARPNESS_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_GAMMA, + .name = "Gamma", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_GAMMA_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_BACKLIGHT_COMPENSATION, + .name = "Backlight Compensation", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_BACKLIGHT_COMPENSATION_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_GAIN, + .name = "Gain", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_GAIN_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .name = "Power Line Frequency", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_POWER_LINE_FREQUENCY_CONTROL, + .size = 2, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_ENUM, + .menu_info = power_line_frequency_controls, + .menu_count = ARRAY_SIZE(power_line_frequency_controls), + }, + { + .id = V4L2_CID_HUE_AUTO, + .name = "Hue, Auto", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_HUE_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, + { + .id = V4L2_CID_EXPOSURE_AUTO, + .name = "Exposure, Auto", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_AE_MODE_CONTROL, + .size = 4, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_BITMASK, + .menu_info = exposure_auto_controls, + .menu_count = ARRAY_SIZE(exposure_auto_controls), + }, + { + .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY, + .name = "Exposure, Auto Priority", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_AE_PRIORITY_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, + { + .id = V4L2_CID_EXPOSURE_ABSOLUTE, + .name = "Exposure (Absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, + .size = 32, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .name = "White Balance Temperature, Auto", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, + { + .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, + .name = "White Balance Temperature", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .name = "White Balance Component, Auto", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .name = "White Balance Blue Component", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_RED_BALANCE, + .name = "White Balance Red Component", + .entity = UVC_GUID_UVC_PROCESSING, + .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .size = 16, + .offset = 16, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + }, + { + .id = V4L2_CID_FOCUS_ABSOLUTE, + .name = "Focus (absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_FOCUS_ABSOLUTE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_FOCUS_AUTO, + .name = "Focus, Auto", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_FOCUS_AUTO_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, +}; + +/* ------------------------------------------------------------------------ + * Utility functions + */ + +static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) +{ + return ctrl->data + id * ctrl->info->size; +} + +static inline int uvc_get_bit(const __u8 *data, int bit) +{ + return (data[bit >> 3] >> (bit & 7)) & 1; +} + +/* Extract the bit string specified by mapping->offset and mapping->size + * from the little-endian data stored at 'data' and return the result as + * a signed 32bit integer. Sign extension will be performed if the mapping + * references a signed data type. + */ +static __s32 uvc_get_le_value(const __u8 *data, + struct uvc_control_mapping *mapping) +{ + int bits = mapping->size; + int offset = mapping->offset; + __s32 value = 0; + __u8 mask; + + data += offset / 8; + offset &= 7; + mask = ((1LL << bits) - 1) << offset; + + for (; bits > 0; data++) { + __u8 byte = *data & mask; + value |= offset > 0 ? (byte >> offset) : (byte << (-offset)); + bits -= 8 - (offset > 0 ? offset : 0); + offset -= 8; + mask = (1 << bits) - 1; + } + + /* Sign-extend the value if needed */ + if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED) + value |= -(value & (1 << (mapping->size - 1))); + + return value; +} + +/* Set the bit string specified by mapping->offset and mapping->size + * in the little-endian data stored at 'data' to the value 'value'. + */ +static void uvc_set_le_value(__s32 value, __u8 *data, + struct uvc_control_mapping *mapping) +{ + int bits = mapping->size; + int offset = mapping->offset; + __u8 mask; + + data += offset / 8; + offset &= 7; + + for (; bits > 0; data++) { + mask = ((1LL << bits) - 1) << offset; + *data = (*data & ~mask) | ((value << offset) & mask); + value >>= offset ? offset : 8; + bits -= 8 - offset; + offset = 0; + } +} + +/* ------------------------------------------------------------------------ + * Terminal and unit management + */ + +static const __u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; +static const __u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; +static const __u8 uvc_media_transport_input_guid[16] = + UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; + +static int uvc_entity_match_guid(struct uvc_entity *entity, __u8 guid[16]) +{ + switch (UVC_ENTITY_TYPE(entity)) { + case ITT_CAMERA: + return memcmp(uvc_camera_guid, guid, 16) == 0; + + case ITT_MEDIA_TRANSPORT_INPUT: + return memcmp(uvc_media_transport_input_guid, guid, 16) == 0; + + case VC_PROCESSING_UNIT: + return memcmp(uvc_processing_guid, guid, 16) == 0; + + case VC_EXTENSION_UNIT: + return memcmp(entity->extension.guidExtensionCode, + guid, 16) == 0; + + default: + return 0; + } +} + +/* ------------------------------------------------------------------------ + * UVC Controls + */ + +static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id, + struct uvc_control_mapping **mapping, struct uvc_control **control, + int next) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *map; + unsigned int i; + + if (entity == NULL) + return; + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (ctrl->info == NULL) + continue; + + list_for_each_entry(map, &ctrl->info->mappings, list) { + if ((map->id == v4l2_id) && !next) { + *control = ctrl; + *mapping = map; + return; + } + + if ((*mapping == NULL || (*mapping)->id > map->id) && + (map->id > v4l2_id) && next) { + *control = ctrl; + *mapping = map; + } + } + } +} + +struct uvc_control *uvc_find_control(struct uvc_video_device *video, + __u32 v4l2_id, struct uvc_control_mapping **mapping) +{ + struct uvc_control *ctrl = NULL; + struct uvc_entity *entity; + int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL; + + *mapping = NULL; + + /* Mask the query flags. */ + v4l2_id &= V4L2_CTRL_ID_MASK; + + /* Find the control. */ + __uvc_find_control(video->processing, v4l2_id, mapping, &ctrl, next); + if (ctrl && !next) + return ctrl; + + list_for_each_entry(entity, &video->iterms, chain) { + __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); + if (ctrl && !next) + return ctrl; + } + + list_for_each_entry(entity, &video->extensions, chain) { + __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); + if (ctrl && !next) + return ctrl; + } + + if (ctrl == NULL && !next) + uvc_trace(UVC_TRACE_CONTROL, "Control 0x%08x not found.\n", + v4l2_id); + + return ctrl; +} + +int uvc_query_v4l2_ctrl(struct uvc_video_device *video, + struct v4l2_queryctrl *v4l2_ctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + struct uvc_menu_info *menu; + unsigned int i; + __u8 data[8]; + int ret; + + ctrl = uvc_find_control(video, v4l2_ctrl->id, &mapping); + if (ctrl == NULL) + return -EINVAL; + + v4l2_ctrl->id = mapping->id; + v4l2_ctrl->type = mapping->v4l2_type; + strncpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); + v4l2_ctrl->flags = 0; + + if (!(ctrl->info->flags & UVC_CONTROL_SET_CUR)) + v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (ctrl->info->flags & UVC_CONTROL_GET_DEF) { + if ((ret = uvc_query_ctrl(video->dev, GET_DEF, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + &data, ctrl->info->size)) < 0) + return ret; + v4l2_ctrl->default_value = uvc_get_le_value(data, mapping); + } + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + v4l2_ctrl->minimum = 0; + v4l2_ctrl->maximum = mapping->menu_count - 1; + v4l2_ctrl->step = 1; + + menu = mapping->menu_info; + for (i = 0; i < mapping->menu_count; ++i, ++menu) { + if (menu->value == v4l2_ctrl->default_value) { + v4l2_ctrl->default_value = i; + break; + } + } + + return 0; + } + + if (ctrl->info->flags & UVC_CONTROL_GET_MIN) { + if ((ret = uvc_query_ctrl(video->dev, GET_MIN, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + &data, ctrl->info->size)) < 0) + return ret; + v4l2_ctrl->minimum = uvc_get_le_value(data, mapping); + } + if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { + if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + &data, ctrl->info->size)) < 0) + return ret; + v4l2_ctrl->maximum = uvc_get_le_value(data, mapping); + } + if (ctrl->info->flags & UVC_CONTROL_GET_RES) { + if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + &data, ctrl->info->size)) < 0) + return ret; + v4l2_ctrl->step = uvc_get_le_value(data, mapping); + } + + return 0; +} + + +/* -------------------------------------------------------------------------- + * Control transactions + * + * To make extended set operations as atomic as the hardware allows, controls + * are handled using begin/commit/rollback operations. + * + * At the beginning of a set request, uvc_ctrl_begin should be called to + * initialize the request. This function acquires the control lock. + * + * When setting a control, the new value is stored in the control data field + * at position UVC_CTRL_DATA_CURRENT. The control is then marked as dirty for + * later processing. If the UVC and V4L2 control sizes differ, the current + * value is loaded from the hardware before storing the new value in the data + * field. + * + * After processing all controls in the transaction, uvc_ctrl_commit or + * uvc_ctrl_rollback must be called to apply the pending changes to the + * hardware or revert them. When applying changes, all controls marked as + * dirty will be modified in the UVC device, and the dirty flag will be + * cleared. When reverting controls, the control data field + * UVC_CTRL_DATA_CURRENT is reverted to its previous value + * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the + * control lock. + */ +int uvc_ctrl_begin(struct uvc_video_device *video) +{ + return mutex_lock_interruptible(&video->ctrl_mutex) ? -ERESTARTSYS : 0; +} + +static int uvc_ctrl_commit_entity(struct uvc_device *dev, + struct uvc_entity *entity, int rollback) +{ + struct uvc_control *ctrl; + unsigned int i; + int ret; + + if (entity == NULL) + return 0; + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (ctrl->info == NULL || !ctrl->dirty) + continue; + + if (!rollback) + ret = uvc_query_ctrl(dev, SET_CUR, ctrl->entity->id, + dev->intfnum, ctrl->info->selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info->size); + else + ret = 0; + + if (rollback || ret < 0) + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), + ctrl->info->size); + + if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) + ctrl->loaded = 0; + + ctrl->dirty = 0; + + if (ret < 0) + return ret; + } + + return 0; +} + +int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback) +{ + struct uvc_entity *entity; + int ret = 0; + + /* Find the control. */ + ret = uvc_ctrl_commit_entity(video->dev, video->processing, rollback); + if (ret < 0) + goto done; + + list_for_each_entry(entity, &video->iterms, chain) { + ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); + if (ret < 0) + goto done; + } + + list_for_each_entry(entity, &video->extensions, chain) { + ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); + if (ret < 0) + goto done; + } + +done: + mutex_unlock(&video->ctrl_mutex); + return ret; +} + +int uvc_ctrl_get(struct uvc_video_device *video, + struct v4l2_ext_control *xctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + struct uvc_menu_info *menu; + unsigned int i; + int ret; + + ctrl = uvc_find_control(video, xctrl->id, &mapping); + if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) + return -EINVAL; + + if (!ctrl->loaded) { + ret = uvc_query_ctrl(video->dev, GET_CUR, ctrl->entity->id, + video->dev->intfnum, ctrl->info->selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info->size); + if (ret < 0) + return ret; + + if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0) + ctrl->loaded = 1; + } + + xctrl->value = uvc_get_le_value( + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + menu = mapping->menu_info; + for (i = 0; i < mapping->menu_count; ++i, ++menu) { + if (menu->value == xctrl->value) { + xctrl->value = i; + break; + } + } + } + + return 0; +} + +int uvc_ctrl_set(struct uvc_video_device *video, + struct v4l2_ext_control *xctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + s32 value = xctrl->value; + int ret; + + ctrl = uvc_find_control(video, xctrl->id, &mapping); + if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0) + return -EINVAL; + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + if (value < 0 || value >= mapping->menu_count) + return -EINVAL; + value = mapping->menu_info[value].value; + } + + if (!ctrl->loaded && (ctrl->info->size * 8) != mapping->size) { + if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) { + memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + 0, ctrl->info->size); + } else { + ret = uvc_query_ctrl(video->dev, GET_CUR, + ctrl->entity->id, video->dev->intfnum, + ctrl->info->selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info->size); + if (ret < 0) + return ret; + } + + if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0) + ctrl->loaded = 1; + } + + if (!ctrl->dirty) { + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info->size); + } + + uvc_set_le_value(value, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); + + ctrl->dirty = 1; + ctrl->modified = 1; + return 0; +} + +/* -------------------------------------------------------------------------- + * Dynamic controls + */ + +int uvc_xu_ctrl_query(struct uvc_video_device *video, + struct uvc_xu_control *xctrl, int set) +{ + struct uvc_entity *entity; + struct uvc_control *ctrl = NULL; + unsigned int i, found = 0; + __u8 *data; + int ret; + + /* Find the extension unit. */ + list_for_each_entry(entity, &video->extensions, chain) { + if (entity->id == xctrl->unit) + break; + } + + if (entity->id != xctrl->unit) { + uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n", + xctrl->unit); + return -EINVAL; + } + + /* Find the control. */ + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (ctrl->info == NULL) + continue; + + if (ctrl->info->selector == xctrl->selector) { + found = 1; + break; + } + } + + if (!found) { + uvc_trace(UVC_TRACE_CONTROL, + "Control " UVC_GUID_FORMAT "/%u not found.\n", + UVC_GUID_ARGS(entity->extension.guidExtensionCode), + xctrl->selector); + return -EINVAL; + } + + /* Validate control data size. */ + if (ctrl->info->size != xctrl->size) + return -EINVAL; + + if ((set && !(ctrl->info->flags & UVC_CONTROL_SET_CUR)) || + (!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR))) + return -EINVAL; + + if (mutex_lock_interruptible(&video->ctrl_mutex)) + return -ERESTARTSYS; + + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + xctrl->size); + data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT); + + if (set && copy_from_user(data, xctrl->data, xctrl->size)) { + ret = -EFAULT; + goto out; + } + + ret = uvc_query_ctrl(video->dev, set ? SET_CUR : GET_CUR, xctrl->unit, + video->dev->intfnum, xctrl->selector, data, + xctrl->size); + if (ret < 0) + goto out; + + if (!set && copy_to_user(xctrl->data, data, xctrl->size)) { + ret = -EFAULT; + goto out; + } + +out: + if (ret) + memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), + xctrl->size); + + mutex_unlock(&video->ctrl_mutex); + return ret; +} + +/* -------------------------------------------------------------------------- + * Suspend/resume + */ + +/* + * Restore control values after resume, skipping controls that haven't been + * changed. + * + * TODO + * - Don't restore modified controls that are back to their default value. + * - Handle restore order (Auto-Exposure Mode should be restored before + * Exposure Time). + */ +int uvc_ctrl_resume_device(struct uvc_device *dev) +{ + struct uvc_control *ctrl; + struct uvc_entity *entity; + unsigned int i; + int ret; + + /* Walk the entities list and restore controls when possible. */ + list_for_each_entry(entity, &dev->entities, list) { + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + + if (ctrl->info == NULL || !ctrl->modified || + (ctrl->info->flags & UVC_CONTROL_RESTORE) == 0) + continue; + + printk(KERN_INFO "restoring control " UVC_GUID_FORMAT + "/%u/%u\n", UVC_GUID_ARGS(ctrl->info->entity), + ctrl->info->index, ctrl->info->selector); + ctrl->dirty = 1; + } + + ret = uvc_ctrl_commit_entity(dev, entity, 0); + if (ret < 0) + return ret; + } + + return 0; +} + +/* -------------------------------------------------------------------------- + * Control and mapping handling + */ + +static void uvc_ctrl_add_ctrl(struct uvc_device *dev, + struct uvc_control_info *info) +{ + struct uvc_entity *entity; + struct uvc_control *ctrl = NULL; + int ret, found = 0; + unsigned int i; + + list_for_each_entry(entity, &dev->entities, list) { + if (!uvc_entity_match_guid(entity, info->entity)) + continue; + + for (i = 0; i < entity->ncontrols; ++i) { + ctrl = &entity->controls[i]; + if (ctrl->index == info->index) { + found = 1; + break; + } + } + + if (found) + break; + } + + if (!found) + return; + + if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) { + /* Check if the device control information and length match + * the user supplied information. + */ + __u32 flags; + __le16 size; + __u8 inf; + + if ((ret = uvc_query_ctrl(dev, GET_LEN, ctrl->entity->id, + dev->intfnum, info->selector, (__u8 *)&size, 2)) < 0) { + uvc_trace(UVC_TRACE_CONTROL, "GET_LEN failed on " + "control " UVC_GUID_FORMAT "/%u (%d).\n", + UVC_GUID_ARGS(info->entity), info->selector, + ret); + return; + } + + if (info->size != le16_to_cpu(size)) { + uvc_trace(UVC_TRACE_CONTROL, "Control " UVC_GUID_FORMAT + "/%u size doesn't match user supplied " + "value.\n", UVC_GUID_ARGS(info->entity), + info->selector); + return; + } + + if ((ret = uvc_query_ctrl(dev, GET_INFO, ctrl->entity->id, + dev->intfnum, info->selector, &inf, 1)) < 0) { + uvc_trace(UVC_TRACE_CONTROL, "GET_INFO failed on " + "control " UVC_GUID_FORMAT "/%u (%d).\n", + UVC_GUID_ARGS(info->entity), info->selector, + ret); + return; + } + + flags = info->flags; + if (((flags & UVC_CONTROL_GET_CUR) && !(inf & (1 << 0))) || + ((flags & UVC_CONTROL_SET_CUR) && !(inf & (1 << 1)))) { + uvc_trace(UVC_TRACE_CONTROL, "Control " + UVC_GUID_FORMAT "/%u flags don't match " + "supported operations.\n", + UVC_GUID_ARGS(info->entity), info->selector); + return; + } + } + + ctrl->info = info; + ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_NDATA, GFP_KERNEL); + uvc_trace(UVC_TRACE_CONTROL, "Added control " UVC_GUID_FORMAT "/%u " + "to device %s entity %u\n", UVC_GUID_ARGS(ctrl->info->entity), + ctrl->info->selector, dev->udev->devpath, entity->id); +} + +/* + * Add an item to the UVC control information list, and instantiate a control + * structure for each device that supports the control. + */ +int uvc_ctrl_add_info(struct uvc_control_info *info) +{ + struct uvc_control_info *ctrl; + struct uvc_device *dev; + int ret = 0; + + /* Find matching controls by walking the devices, entities and + * controls list. + */ + mutex_lock(&uvc_driver.ctrl_mutex); + + /* First check if the list contains a control matching the new one. + * Bail out if it does. + */ + list_for_each_entry(ctrl, &uvc_driver.controls, list) { + if (memcmp(ctrl->entity, info->entity, 16)) + continue; + + if (ctrl->selector == info->selector) { + uvc_trace(UVC_TRACE_CONTROL, "Control " + UVC_GUID_FORMAT "/%u is already defined.\n", + UVC_GUID_ARGS(info->entity), info->selector); + ret = -EEXIST; + goto end; + } + if (ctrl->index == info->index) { + uvc_trace(UVC_TRACE_CONTROL, "Control " + UVC_GUID_FORMAT "/%u would overwrite index " + "%d.\n", UVC_GUID_ARGS(info->entity), + info->selector, info->index); + ret = -EEXIST; + goto end; + } + } + + list_for_each_entry(dev, &uvc_driver.devices, list) + uvc_ctrl_add_ctrl(dev, info); + + INIT_LIST_HEAD(&info->mappings); + list_add_tail(&info->list, &uvc_driver.controls); +end: + mutex_unlock(&uvc_driver.ctrl_mutex); + return ret; +} + +int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping) +{ + struct uvc_control_info *info; + struct uvc_control_mapping *map; + int ret = -EINVAL; + + if (mapping->id & ~V4L2_CTRL_ID_MASK) { + uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with " + "invalid control id 0x%08x\n", mapping->name, + mapping->id); + return -EINVAL; + } + + mutex_lock(&uvc_driver.ctrl_mutex); + list_for_each_entry(info, &uvc_driver.controls, list) { + if (memcmp(info->entity, mapping->entity, 16) || + info->selector != mapping->selector) + continue; + + if (info->size * 8 < mapping->size + mapping->offset) { + uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' would " + "overflow control " UVC_GUID_FORMAT "/%u\n", + mapping->name, UVC_GUID_ARGS(info->entity), + info->selector); + ret = -EOVERFLOW; + goto end; + } + + /* Check if the list contains a mapping matching the new one. + * Bail out if it does. + */ + list_for_each_entry(map, &info->mappings, list) { + if (map->id == mapping->id) { + uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' is " + "already defined.\n", mapping->name); + ret = -EEXIST; + goto end; + } + } + + mapping->ctrl = info; + list_add_tail(&mapping->list, &info->mappings); + uvc_trace(UVC_TRACE_CONTROL, "Adding mapping %s to control " + UVC_GUID_FORMAT "/%u.\n", mapping->name, + UVC_GUID_ARGS(info->entity), info->selector); + + ret = 0; + break; + } +end: + mutex_unlock(&uvc_driver.ctrl_mutex); + return ret; +} + +/* + * Initialize device controls. + */ +int uvc_ctrl_init_device(struct uvc_device *dev) +{ + struct uvc_control_info *info; + struct uvc_control *ctrl; + struct uvc_entity *entity; + unsigned int i; + + /* Walk the entities list and instantiate controls */ + list_for_each_entry(entity, &dev->entities, list) { + unsigned int bControlSize = 0, ncontrols = 0; + __u8 *bmControls = NULL; + + if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) { + bmControls = entity->extension.bmControls; + bControlSize = entity->extension.bControlSize; + } else if (UVC_ENTITY_TYPE(entity) == VC_PROCESSING_UNIT) { + bmControls = entity->processing.bmControls; + bControlSize = entity->processing.bControlSize; + } else if (UVC_ENTITY_TYPE(entity) == ITT_CAMERA) { + bmControls = entity->camera.bmControls; + bControlSize = entity->camera.bControlSize; + } + + for (i = 0; i < bControlSize; ++i) + ncontrols += hweight8(bmControls[i]); + + if (ncontrols == 0) + continue; + + entity->controls = kzalloc(ncontrols*sizeof *ctrl, GFP_KERNEL); + if (entity->controls == NULL) + return -ENOMEM; + + entity->ncontrols = ncontrols; + + ctrl = entity->controls; + for (i = 0; i < bControlSize * 8; ++i) { + if (uvc_get_bit(bmControls, i) == 0) + continue; + + ctrl->entity = entity; + ctrl->index = i; + ctrl++; + } + } + + /* Walk the controls info list and associate them with the device + * controls, then add the device to the global device list. This has + * to be done while holding the controls lock, to make sure + * uvc_ctrl_add_info() will not get called in-between. + */ + mutex_lock(&uvc_driver.ctrl_mutex); + list_for_each_entry(info, &uvc_driver.controls, list) + uvc_ctrl_add_ctrl(dev, info); + + list_add_tail(&dev->list, &uvc_driver.devices); + mutex_unlock(&uvc_driver.ctrl_mutex); + + return 0; +} + +/* + * Cleanup device controls. + */ +void uvc_ctrl_cleanup_device(struct uvc_device *dev) +{ + struct uvc_entity *entity; + unsigned int i; + + /* Remove the device from the global devices list */ + mutex_lock(&uvc_driver.ctrl_mutex); + if (dev->list.next != NULL) + list_del(&dev->list); + mutex_unlock(&uvc_driver.ctrl_mutex); + + list_for_each_entry(entity, &dev->entities, list) { + for (i = 0; i < entity->ncontrols; ++i) + kfree(entity->controls[i].data); + + kfree(entity->controls); + } +} + +void uvc_ctrl_init(void) +{ + struct uvc_control_info *ctrl = uvc_ctrls; + struct uvc_control_info *cend = ctrl + ARRAY_SIZE(uvc_ctrls); + struct uvc_control_mapping *mapping = uvc_ctrl_mappings; + struct uvc_control_mapping *mend = + mapping + ARRAY_SIZE(uvc_ctrl_mappings); + + for (; ctrl < cend; ++ctrl) + uvc_ctrl_add_info(ctrl); + + for (; mapping < mend; ++mapping) + uvc_ctrl_add_mapping(mapping); +} diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c new file mode 100644 index 0000000..60ced58 --- /dev/null +++ b/drivers/media/video/uvc/uvc_driver.c @@ -0,0 +1,1955 @@ +/* + * uvc_driver.c -- USB Video Class driver + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +/* + * This driver aims to support video input devices compliant with the 'USB + * Video Class' specification. + * + * The driver doesn't support the deprecated v4l1 interface. It implements the + * mmap capture method only, and doesn't do any image format conversion in + * software. If your user-space application doesn't support YUYV or MJPEG, fix + * it :-). Please note that the MJPEG data have been stripped from their + * Huffman tables (DHT marker), you will need to add it back if your JPEG + * codec can't handle MJPEG data. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "uvcvideo.h" + +#define DRIVER_AUTHOR "Laurent Pinchart " +#define DRIVER_DESC "USB Video Class driver" +#ifndef DRIVER_VERSION +#define DRIVER_VERSION "v0.1.0" +#endif + +static unsigned int uvc_quirks_param; +unsigned int uvc_trace_param; + +/* ------------------------------------------------------------------------ + * Control, formats, ... + */ + +static struct uvc_format_desc uvc_fmts[] = { + { + .name = "YUV 4:2:2 (YUYV)", + .guid = UVC_GUID_FORMAT_YUY2, + .fcc = V4L2_PIX_FMT_YUYV, + }, + { + .name = "YUV 4:2:0 (NV12)", + .guid = UVC_GUID_FORMAT_NV12, + .fcc = V4L2_PIX_FMT_NV12, + }, + { + .name = "MJPEG", + .guid = UVC_GUID_FORMAT_MJPEG, + .fcc = V4L2_PIX_FMT_MJPEG, + }, + { + .name = "YVU 4:2:0 (YV12)", + .guid = UVC_GUID_FORMAT_YV12, + .fcc = V4L2_PIX_FMT_YVU420, + }, + { + .name = "YUV 4:2:0 (I420)", + .guid = UVC_GUID_FORMAT_I420, + .fcc = V4L2_PIX_FMT_YUV420, + }, + { + .name = "YUV 4:2:2 (UYVY)", + .guid = UVC_GUID_FORMAT_UYVY, + .fcc = V4L2_PIX_FMT_UYVY, + }, + { + .name = "Greyscale", + .guid = UVC_GUID_FORMAT_Y800, + .fcc = V4L2_PIX_FMT_GREY, + }, + { + .name = "RGB Bayer", + .guid = UVC_GUID_FORMAT_BY8, + .fcc = V4L2_PIX_FMT_SBGGR8, + }, +}; + +/* ------------------------------------------------------------------------ + * Utility functions + */ + +struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, + __u8 epaddr) +{ + struct usb_host_endpoint *ep; + unsigned int i; + + for (i = 0; i < alts->desc.bNumEndpoints; ++i) { + ep = &alts->endpoint[i]; + if (ep->desc.bEndpointAddress == epaddr) + return ep; + } + + return NULL; +} + +static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16]) +{ + unsigned int len = ARRAY_SIZE(uvc_fmts); + unsigned int i; + + for (i = 0; i < len; ++i) { + if (memcmp(guid, uvc_fmts[i].guid, 16) == 0) + return &uvc_fmts[i]; + } + + return NULL; +} + +static __u32 uvc_colorspace(const __u8 primaries) +{ + static const __u8 colorprimaries[] = { + 0, + V4L2_COLORSPACE_SRGB, + V4L2_COLORSPACE_470_SYSTEM_M, + V4L2_COLORSPACE_470_SYSTEM_BG, + V4L2_COLORSPACE_SMPTE170M, + V4L2_COLORSPACE_SMPTE240M, + }; + + if (primaries < ARRAY_SIZE(colorprimaries)) + return colorprimaries[primaries]; + + return 0; +} + +/* Simplify a fraction using a simple continued fraction decomposition. The + * idea here is to convert fractions such as 333333/10000000 to 1/30 using + * 32 bit arithmetic only. The algorithm is not perfect and relies upon two + * arbitrary parameters to remove non-significative terms from the simple + * continued fraction decomposition. Using 8 and 333 for n_terms and threshold + * respectively seems to give nice results. + */ +void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, + unsigned int n_terms, unsigned int threshold) +{ + uint32_t *an; + uint32_t x, y, r; + unsigned int i, n; + + an = kmalloc(n_terms * sizeof *an, GFP_KERNEL); + if (an == NULL) + return; + + /* Convert the fraction to a simple continued fraction. See + * http://mathforum.org/dr.math/faq/faq.fractions.html + * Stop if the current term is bigger than or equal to the given + * threshold. + */ + x = *numerator; + y = *denominator; + + for (n = 0; n < n_terms && y != 0; ++n) { + an[n] = x / y; + if (an[n] >= threshold) { + if (n < 2) + n++; + break; + } + + r = x - an[n] * y; + x = y; + y = r; + } + + /* Expand the simple continued fraction back to an integer fraction. */ + x = 0; + y = 1; + + for (i = n; i > 0; --i) { + r = y; + y = an[i-1] * y + x; + x = r; + } + + *numerator = y; + *denominator = x; + kfree(an); +} + +/* Convert a fraction to a frame interval in 100ns multiples. The idea here is + * to compute numerator / denominator * 10000000 using 32 bit fixed point + * arithmetic only. + */ +uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator) +{ + uint32_t multiplier; + + /* Saturate the result if the operation would overflow. */ + if (denominator == 0 || + numerator/denominator >= ((uint32_t)-1)/10000000) + return (uint32_t)-1; + + /* Divide both the denominator and the multiplier by two until + * numerator * multiplier doesn't overflow. If anyone knows a better + * algorithm please let me know. + */ + multiplier = 10000000; + while (numerator > ((uint32_t)-1)/multiplier) { + multiplier /= 2; + denominator /= 2; + } + + return denominator ? numerator * multiplier / denominator : 0; +} + +/* ------------------------------------------------------------------------ + * Terminal and unit management + */ + +static struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) +{ + struct uvc_entity *entity; + + list_for_each_entry(entity, &dev->entities, list) { + if (entity->id == id) + return entity; + } + + return NULL; +} + +static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, + int id, struct uvc_entity *entity) +{ + unsigned int i; + + if (entity == NULL) + entity = list_entry(&dev->entities, struct uvc_entity, list); + + list_for_each_entry_continue(entity, &dev->entities, list) { + switch (UVC_ENTITY_TYPE(entity)) { + case TT_STREAMING: + if (entity->output.bSourceID == id) + return entity; + break; + + case VC_PROCESSING_UNIT: + if (entity->processing.bSourceID == id) + return entity; + break; + + case VC_SELECTOR_UNIT: + for (i = 0; i < entity->selector.bNrInPins; ++i) + if (entity->selector.baSourceID[i] == id) + return entity; + break; + + case VC_EXTENSION_UNIT: + for (i = 0; i < entity->extension.bNrInPins; ++i) + if (entity->extension.baSourceID[i] == id) + return entity; + break; + } + } + + return NULL; +} + +/* ------------------------------------------------------------------------ + * Descriptors handling + */ + +static int uvc_parse_format(struct uvc_device *dev, + struct uvc_streaming *streaming, struct uvc_format *format, + __u32 **intervals, unsigned char *buffer, int buflen) +{ + struct usb_interface *intf = streaming->intf; + struct usb_host_interface *alts = intf->cur_altsetting; + struct uvc_format_desc *fmtdesc; + struct uvc_frame *frame; + const unsigned char *start = buffer; + unsigned int interval; + unsigned int i, n; + __u8 ftype; + + format->type = buffer[2]; + format->index = buffer[3]; + + switch (buffer[2]) { + case VS_FORMAT_UNCOMPRESSED: + case VS_FORMAT_FRAME_BASED: + if (buflen < 27) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + /* Find the format descriptor from its GUID. */ + fmtdesc = uvc_format_by_guid(&buffer[5]); + + if (fmtdesc != NULL) { + strncpy(format->name, fmtdesc->name, + sizeof format->name); + format->fcc = fmtdesc->fcc; + } else { + uvc_printk(KERN_INFO, "Unknown video format " + UVC_GUID_FORMAT "\n", + UVC_GUID_ARGS(&buffer[5])); + snprintf(format->name, sizeof format->name, + UVC_GUID_FORMAT, UVC_GUID_ARGS(&buffer[5])); + format->fcc = 0; + } + + format->bpp = buffer[21]; + if (buffer[2] == VS_FORMAT_UNCOMPRESSED) { + ftype = VS_FRAME_UNCOMPRESSED; + } else { + ftype = VS_FRAME_FRAME_BASED; + if (buffer[27]) + format->flags = UVC_FMT_FLAG_COMPRESSED; + } + break; + + case VS_FORMAT_MJPEG: + if (buflen < 11) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + strncpy(format->name, "MJPEG", sizeof format->name); + format->fcc = V4L2_PIX_FMT_MJPEG; + format->flags = UVC_FMT_FLAG_COMPRESSED; + format->bpp = 0; + ftype = VS_FRAME_MJPEG; + break; + + case VS_FORMAT_DV: + if (buflen < 9) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d FORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + switch (buffer[8] & 0x7f) { + case 0: + strncpy(format->name, "SD-DV", sizeof format->name); + break; + case 1: + strncpy(format->name, "SDL-DV", sizeof format->name); + break; + case 2: + strncpy(format->name, "HD-DV", sizeof format->name); + break; + default: + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d: unknown DV format %u\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber, buffer[8]); + return -EINVAL; + } + + strncat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz", + sizeof format->name); + + format->fcc = V4L2_PIX_FMT_DV; + format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM; + format->bpp = 0; + ftype = 0; + + /* Create a dummy frame descriptor. */ + frame = &format->frame[0]; + memset(&format->frame[0], 0, sizeof format->frame[0]); + frame->bFrameIntervalType = 1; + frame->dwDefaultFrameInterval = 1; + frame->dwFrameInterval = *intervals; + *(*intervals)++ = 1; + format->nframes = 1; + break; + + case VS_FORMAT_MPEG2TS: + case VS_FORMAT_STREAM_BASED: + /* Not supported yet. */ + default: + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d unsupported format %u\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, + buffer[2]); + return -EINVAL; + } + + uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name); + + buflen -= buffer[0]; + buffer += buffer[0]; + + /* Parse the frame descriptors. Only uncompressed, MJPEG and frame + * based formats have frame descriptors. + */ + while (buflen > 2 && buffer[2] == ftype) { + frame = &format->frame[format->nframes]; + + if (ftype != VS_FRAME_FRAME_BASED) + n = buflen > 25 ? buffer[25] : 0; + else + n = buflen > 21 ? buffer[21] : 0; + + n = n ? n : 3; + + if (buflen < 26 + 4*n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d FRAME error\n", dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + frame->bFrameIndex = buffer[3]; + frame->bmCapabilities = buffer[4]; + frame->wWidth = le16_to_cpup((__le16 *)&buffer[5]); + frame->wHeight = le16_to_cpup((__le16 *)&buffer[7]); + frame->dwMinBitRate = le32_to_cpup((__le32 *)&buffer[9]); + frame->dwMaxBitRate = le32_to_cpup((__le32 *)&buffer[13]); + if (ftype != VS_FRAME_FRAME_BASED) { + frame->dwMaxVideoFrameBufferSize = + le32_to_cpup((__le32 *)&buffer[17]); + frame->dwDefaultFrameInterval = + le32_to_cpup((__le32 *)&buffer[21]); + frame->bFrameIntervalType = buffer[25]; + } else { + frame->dwMaxVideoFrameBufferSize = 0; + frame->dwDefaultFrameInterval = + le32_to_cpup((__le32 *)&buffer[17]); + frame->bFrameIntervalType = buffer[21]; + } + frame->dwFrameInterval = *intervals; + + /* Several UVC chipsets screw up dwMaxVideoFrameBufferSize + * completely. Observed behaviours range from setting the + * value to 1.1x the actual frame size of hardwiring the + * 16 low bits to 0. This results in a higher than necessary + * memory usage as well as a wrong image size information. For + * uncompressed formats this can be fixed by computing the + * value from the frame size. + */ + if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) + frame->dwMaxVideoFrameBufferSize = format->bpp + * frame->wWidth * frame->wHeight / 8; + + /* Some bogus devices report dwMinFrameInterval equal to + * dwMaxFrameInterval and have dwFrameIntervalStep set to + * zero. Setting all null intervals to 1 fixes the problem and + * some other divisions by zero which could happen. + */ + for (i = 0; i < n; ++i) { + interval = le32_to_cpup((__le32 *)&buffer[26+4*i]); + *(*intervals)++ = interval ? interval : 1; + } + + /* Make sure that the default frame interval stays between + * the boundaries. + */ + n -= frame->bFrameIntervalType ? 1 : 2; + frame->dwDefaultFrameInterval = + min(frame->dwFrameInterval[n], + max(frame->dwFrameInterval[0], + frame->dwDefaultFrameInterval)); + + uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n", + frame->wWidth, frame->wHeight, + 10000000/frame->dwDefaultFrameInterval, + (100000000/frame->dwDefaultFrameInterval)%10); + + format->nframes++; + buflen -= buffer[0]; + buffer += buffer[0]; + } + + if (buflen > 2 && buffer[2] == VS_STILL_IMAGE_FRAME) { + buflen -= buffer[0]; + buffer += buffer[0]; + } + + if (buflen > 2 && buffer[2] == VS_COLORFORMAT) { + if (buflen < 6) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d COLORFORMAT error\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + format->colorspace = uvc_colorspace(buffer[3]); + + buflen -= buffer[0]; + buffer += buffer[0]; + } + + return buffer - start; +} + +static int uvc_parse_streaming(struct uvc_device *dev, + struct usb_interface *intf) +{ + struct uvc_streaming *streaming = NULL; + struct uvc_format *format; + struct uvc_frame *frame; + struct usb_host_interface *alts = &intf->altsetting[0]; + unsigned char *_buffer, *buffer = alts->extra; + int _buflen, buflen = alts->extralen; + unsigned int nformats = 0, nframes = 0, nintervals = 0; + unsigned int size, i, n, p; + __u32 *interval; + __u16 psize; + int ret = -EINVAL; + + if (intf->cur_altsetting->desc.bInterfaceSubClass + != SC_VIDEOSTREAMING) { + uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a " + "video streaming interface\n", dev->udev->devnum, + intf->altsetting[0].desc.bInterfaceNumber); + return -EINVAL; + } + + if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) { + uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already " + "claimed\n", dev->udev->devnum, + intf->altsetting[0].desc.bInterfaceNumber); + return -EINVAL; + } + + streaming = kzalloc(sizeof *streaming, GFP_KERNEL); + if (streaming == NULL) { + usb_driver_release_interface(&uvc_driver.driver, intf); + return -EINVAL; + } + + mutex_init(&streaming->mutex); + streaming->intf = usb_get_intf(intf); + streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; + + /* The Pico iMage webcam has its class-specific interface descriptors + * after the endpoint descriptors. + */ + if (buflen == 0) { + for (i = 0; i < alts->desc.bNumEndpoints; ++i) { + struct usb_host_endpoint *ep = &alts->endpoint[i]; + + if (ep->extralen == 0) + continue; + + if (ep->extralen > 2 && + ep->extra[1] == USB_DT_CS_INTERFACE) { + uvc_trace(UVC_TRACE_DESCR, "trying extra data " + "from endpoint %u.\n", i); + buffer = alts->endpoint[i].extra; + buflen = alts->endpoint[i].extralen; + break; + } + } + } + + /* Skip the standard interface descriptors. */ + while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) { + buflen -= buffer[0]; + buffer += buffer[0]; + } + + if (buflen <= 2) { + uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming " + "interface descriptors found.\n"); + goto error; + } + + /* Parse the header descriptor. */ + if (buffer[2] == VS_OUTPUT_HEADER) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d OUTPUT HEADER descriptor is not supported.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); + goto error; + } else if (buffer[2] == VS_INPUT_HEADER) { + p = buflen >= 5 ? buffer[3] : 0; + n = buflen >= 12 ? buffer[12] : 0; + + if (buflen < 13 + p*n || buffer[2] != VS_INPUT_HEADER) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d INPUT HEADER descriptor is " + "invalid.\n", dev->udev->devnum, + alts->desc.bInterfaceNumber); + goto error; + } + + streaming->header.bNumFormats = p; + streaming->header.bEndpointAddress = buffer[6]; + streaming->header.bmInfo = buffer[7]; + streaming->header.bTerminalLink = buffer[8]; + streaming->header.bStillCaptureMethod = buffer[9]; + streaming->header.bTriggerSupport = buffer[10]; + streaming->header.bTriggerUsage = buffer[11]; + streaming->header.bControlSize = n; + + streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL); + if (streaming->header.bmaControls == NULL) { + ret = -ENOMEM; + goto error; + } + + memcpy(streaming->header.bmaControls, &buffer[13], p*n); + } else { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d HEADER descriptor not found.\n", dev->udev->devnum, + alts->desc.bInterfaceNumber); + goto error; + } + + buflen -= buffer[0]; + buffer += buffer[0]; + + _buffer = buffer; + _buflen = buflen; + + /* Count the format and frame descriptors. */ + while (_buflen > 2) { + switch (_buffer[2]) { + case VS_FORMAT_UNCOMPRESSED: + case VS_FORMAT_MJPEG: + case VS_FORMAT_FRAME_BASED: + nformats++; + break; + + case VS_FORMAT_DV: + /* DV format has no frame descriptor. We will create a + * dummy frame descriptor with a dummy frame interval. + */ + nformats++; + nframes++; + nintervals++; + break; + + case VS_FORMAT_MPEG2TS: + case VS_FORMAT_STREAM_BASED: + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " + "interface %d FORMAT %u is not supported.\n", + dev->udev->devnum, + alts->desc.bInterfaceNumber, _buffer[2]); + break; + + case VS_FRAME_UNCOMPRESSED: + case VS_FRAME_MJPEG: + nframes++; + if (_buflen > 25) + nintervals += _buffer[25] ? _buffer[25] : 3; + break; + + case VS_FRAME_FRAME_BASED: + nframes++; + if (_buflen > 21) + nintervals += _buffer[21] ? _buffer[21] : 3; + break; + } + + _buflen -= _buffer[0]; + _buffer += _buffer[0]; + } + + if (nformats == 0) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d has no supported formats defined.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); + goto error; + } + + size = nformats * sizeof *format + nframes * sizeof *frame + + nintervals * sizeof *interval; + format = kzalloc(size, GFP_KERNEL); + if (format == NULL) { + ret = -ENOMEM; + goto error; + } + + frame = (struct uvc_frame *)&format[nformats]; + interval = (__u32 *)&frame[nframes]; + + streaming->format = format; + streaming->nformats = nformats; + + /* Parse the format descriptors. */ + while (buflen > 2) { + switch (buffer[2]) { + case VS_FORMAT_UNCOMPRESSED: + case VS_FORMAT_MJPEG: + case VS_FORMAT_DV: + case VS_FORMAT_FRAME_BASED: + format->frame = frame; + ret = uvc_parse_format(dev, streaming, format, + &interval, buffer, buflen); + if (ret < 0) + goto error; + + frame += format->nframes; + format++; + + buflen -= ret; + buffer += ret; + continue; + + default: + break; + } + + buflen -= buffer[0]; + buffer += buffer[0]; + } + + /* Parse the alternate settings to find the maximum bandwidth. */ + for (i = 0; i < intf->num_altsetting; ++i) { + struct usb_host_endpoint *ep; + alts = &intf->altsetting[i]; + ep = uvc_find_endpoint(alts, + streaming->header.bEndpointAddress); + if (ep == NULL) + continue; + + psize = le16_to_cpu(ep->desc.wMaxPacketSize); + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + if (psize > streaming->maxpsize) + streaming->maxpsize = psize; + } + + list_add_tail(&streaming->list, &dev->streaming); + return 0; + +error: + usb_driver_release_interface(&uvc_driver.driver, intf); + usb_put_intf(intf); + kfree(streaming->format); + kfree(streaming->header.bmaControls); + kfree(streaming); + return ret; +} + +/* Parse vendor-specific extensions. */ +static int uvc_parse_vendor_control(struct uvc_device *dev, + const unsigned char *buffer, int buflen) +{ + struct usb_device *udev = dev->udev; + struct usb_host_interface *alts = dev->intf->cur_altsetting; + struct uvc_entity *unit; + unsigned int n, p; + int handled = 0; + + switch (le16_to_cpu(dev->udev->descriptor.idVendor)) { + case 0x046d: /* Logitech */ + if (buffer[1] != 0x41 || buffer[2] != 0x01) + break; + + /* Logitech implements several vendor specific functions + * through vendor specific extension units (LXU). + * + * The LXU descriptors are similar to XU descriptors + * (see "USB Device Video Class for Video Devices", section + * 3.7.2.6 "Extension Unit Descriptor") with the following + * differences: + * + * ---------------------------------------------------------- + * 0 bLength 1 Number + * Size of this descriptor, in bytes: 24+p+n*2 + * ---------------------------------------------------------- + * 23+p+n bmControlsType N Bitmap + * Individual bits in the set are defined: + * 0: Absolute + * 1: Relative + * + * This bitset is mapped exactly the same as bmControls. + * ---------------------------------------------------------- + * 23+p+n*2 bReserved 1 Boolean + * ---------------------------------------------------------- + * 24+p+n*2 iExtension 1 Index + * Index of a string descriptor that describes this + * extension unit. + * ---------------------------------------------------------- + */ + p = buflen >= 22 ? buffer[21] : 0; + n = buflen >= 25 + p ? buffer[22+p] : 0; + + if (buflen < 25 + p + 2*n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d EXTENSION_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + break; + } + + unit = kzalloc(sizeof *unit + p + 2*n, GFP_KERNEL); + if (unit == NULL) + return -ENOMEM; + + unit->id = buffer[3]; + unit->type = VC_EXTENSION_UNIT; + memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); + unit->extension.bNumControls = buffer[20]; + unit->extension.bNrInPins = + le16_to_cpup((__le16 *)&buffer[21]); + unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; + memcpy(unit->extension.baSourceID, &buffer[22], p); + unit->extension.bControlSize = buffer[22+p]; + unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p; + unit->extension.bmControlsType = (__u8 *)unit + sizeof *unit + + p + n; + memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); + + if (buffer[24+p+2*n] != 0) + usb_string(udev, buffer[24+p+2*n], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Extension %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + handled = 1; + break; + } + + return handled; +} + +static int uvc_parse_standard_control(struct uvc_device *dev, + const unsigned char *buffer, int buflen) +{ + struct usb_device *udev = dev->udev; + struct uvc_entity *unit, *term; + struct usb_interface *intf; + struct usb_host_interface *alts = dev->intf->cur_altsetting; + unsigned int i, n, p, len; + __u16 type; + + switch (buffer[2]) { + case VC_HEADER: + n = buflen >= 12 ? buffer[11] : 0; + + if (buflen < 12 || buflen < 12 + n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d HEADER error\n", udev->devnum, + alts->desc.bInterfaceNumber); + return -EINVAL; + } + + dev->uvc_version = le16_to_cpup((__le16 *)&buffer[3]); + dev->clock_frequency = le32_to_cpup((__le32 *)&buffer[7]); + + /* Parse all USB Video Streaming interfaces. */ + for (i = 0; i < n; ++i) { + intf = usb_ifnum_to_if(udev, buffer[12+i]); + if (intf == NULL) { + uvc_trace(UVC_TRACE_DESCR, "device %d " + "interface %d doesn't exists\n", + udev->devnum, i); + continue; + } + + uvc_parse_streaming(dev, intf); + } + break; + + case VC_INPUT_TERMINAL: + if (buflen < 8) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d INPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + /* Make sure the terminal type MSB is not null, otherwise it + * could be confused with a unit. + */ + type = le16_to_cpup((__le16 *)&buffer[4]); + if ((type & 0xff00) == 0) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d INPUT_TERMINAL %d has invalid " + "type 0x%04x, skipping\n", udev->devnum, + alts->desc.bInterfaceNumber, + buffer[3], type); + return 0; + } + + n = 0; + p = 0; + len = 8; + + if (type == ITT_CAMERA) { + n = buflen >= 15 ? buffer[14] : 0; + len = 15; + + } else if (type == ITT_MEDIA_TRANSPORT_INPUT) { + n = buflen >= 9 ? buffer[8] : 0; + p = buflen >= 10 + n ? buffer[9+n] : 0; + len = 10; + } + + if (buflen < len + n + p) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d INPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + term = kzalloc(sizeof *term + n + p, GFP_KERNEL); + if (term == NULL) + return -ENOMEM; + + term->id = buffer[3]; + term->type = type | UVC_TERM_INPUT; + + if (UVC_ENTITY_TYPE(term) == ITT_CAMERA) { + term->camera.bControlSize = n; + term->camera.bmControls = (__u8 *)term + sizeof *term; + term->camera.wObjectiveFocalLengthMin = + le16_to_cpup((__le16 *)&buffer[8]); + term->camera.wObjectiveFocalLengthMax = + le16_to_cpup((__le16 *)&buffer[10]); + term->camera.wOcularFocalLength = + le16_to_cpup((__le16 *)&buffer[12]); + memcpy(term->camera.bmControls, &buffer[15], n); + } else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) { + term->media.bControlSize = n; + term->media.bmControls = (__u8 *)term + sizeof *term; + term->media.bTransportModeSize = p; + term->media.bmTransportModes = (__u8 *)term + + sizeof *term + n; + memcpy(term->media.bmControls, &buffer[9], n); + memcpy(term->media.bmTransportModes, &buffer[10+n], p); + } + + if (buffer[7] != 0) + usb_string(udev, buffer[7], term->name, + sizeof term->name); + else if (UVC_ENTITY_TYPE(term) == ITT_CAMERA) + sprintf(term->name, "Camera %u", buffer[3]); + else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) + sprintf(term->name, "Media %u", buffer[3]); + else + sprintf(term->name, "Input %u", buffer[3]); + + list_add_tail(&term->list, &dev->entities); + break; + + case VC_OUTPUT_TERMINAL: + if (buflen < 9) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d OUTPUT_TERMINAL error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + /* Make sure the terminal type MSB is not null, otherwise it + * could be confused with a unit. + */ + type = le16_to_cpup((__le16 *)&buffer[4]); + if ((type & 0xff00) == 0) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d OUTPUT_TERMINAL %d has invalid " + "type 0x%04x, skipping\n", udev->devnum, + alts->desc.bInterfaceNumber, buffer[3], type); + return 0; + } + + term = kzalloc(sizeof *term, GFP_KERNEL); + if (term == NULL) + return -ENOMEM; + + term->id = buffer[3]; + term->type = type | UVC_TERM_OUTPUT; + term->output.bSourceID = buffer[7]; + + if (buffer[8] != 0) + usb_string(udev, buffer[8], term->name, + sizeof term->name); + else + sprintf(term->name, "Output %u", buffer[3]); + + list_add_tail(&term->list, &dev->entities); + break; + + case VC_SELECTOR_UNIT: + p = buflen >= 5 ? buffer[4] : 0; + + if (buflen < 5 || buflen < 6 + p) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d SELECTOR_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + unit = kzalloc(sizeof *unit + p, GFP_KERNEL); + if (unit == NULL) + return -ENOMEM; + + unit->id = buffer[3]; + unit->type = buffer[2]; + unit->selector.bNrInPins = buffer[4]; + unit->selector.baSourceID = (__u8 *)unit + sizeof *unit; + memcpy(unit->selector.baSourceID, &buffer[5], p); + + if (buffer[5+p] != 0) + usb_string(udev, buffer[5+p], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Selector %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + break; + + case VC_PROCESSING_UNIT: + n = buflen >= 8 ? buffer[7] : 0; + p = dev->uvc_version >= 0x0110 ? 10 : 9; + + if (buflen < p + n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d PROCESSING_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + unit = kzalloc(sizeof *unit + n, GFP_KERNEL); + if (unit == NULL) + return -ENOMEM; + + unit->id = buffer[3]; + unit->type = buffer[2]; + unit->processing.bSourceID = buffer[4]; + unit->processing.wMaxMultiplier = + le16_to_cpup((__le16 *)&buffer[5]); + unit->processing.bControlSize = buffer[7]; + unit->processing.bmControls = (__u8 *)unit + sizeof *unit; + memcpy(unit->processing.bmControls, &buffer[8], n); + if (dev->uvc_version >= 0x0110) + unit->processing.bmVideoStandards = buffer[9+n]; + + if (buffer[8+n] != 0) + usb_string(udev, buffer[8+n], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Processing %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + break; + + case VC_EXTENSION_UNIT: + p = buflen >= 22 ? buffer[21] : 0; + n = buflen >= 24 + p ? buffer[22+p] : 0; + + if (buflen < 24 + p + n) { + uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " + "interface %d EXTENSION_UNIT error\n", + udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + unit = kzalloc(sizeof *unit + p + n, GFP_KERNEL); + if (unit == NULL) + return -ENOMEM; + + unit->id = buffer[3]; + unit->type = buffer[2]; + memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); + unit->extension.bNumControls = buffer[20]; + unit->extension.bNrInPins = + le16_to_cpup((__le16 *)&buffer[21]); + unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; + memcpy(unit->extension.baSourceID, &buffer[22], p); + unit->extension.bControlSize = buffer[22+p]; + unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p; + memcpy(unit->extension.bmControls, &buffer[23+p], n); + + if (buffer[23+p+n] != 0) + usb_string(udev, buffer[23+p+n], unit->name, + sizeof unit->name); + else + sprintf(unit->name, "Extension %u", buffer[3]); + + list_add_tail(&unit->list, &dev->entities); + break; + + default: + uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE " + "descriptor (%u)\n", buffer[2]); + break; + } + + return 0; +} + +static int uvc_parse_control(struct uvc_device *dev) +{ + struct usb_host_interface *alts = dev->intf->cur_altsetting; + unsigned char *buffer = alts->extra; + int buflen = alts->extralen; + int ret; + + /* Parse the default alternate setting only, as the UVC specification + * defines a single alternate setting, the default alternate setting + * zero. + */ + + while (buflen > 2) { + if (uvc_parse_vendor_control(dev, buffer, buflen) || + buffer[1] != USB_DT_CS_INTERFACE) + goto next_descriptor; + + if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0) + return ret; + +next_descriptor: + buflen -= buffer[0]; + buffer += buffer[0]; + } + + /* Check if the optional status endpoint is present. */ + if (alts->desc.bNumEndpoints == 1) { + struct usb_host_endpoint *ep = &alts->endpoint[0]; + struct usb_endpoint_descriptor *desc = &ep->desc; + + if (usb_endpoint_is_int_in(desc) && + le16_to_cpu(desc->wMaxPacketSize) >= 8 && + desc->bInterval != 0) { + uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint " + "(addr %02x).\n", desc->bEndpointAddress); + dev->int_ep = ep; + } + } + + return 0; +} + +/* ------------------------------------------------------------------------ + * USB probe and disconnect + */ + +/* + * Unregister the video devices. + */ +static void uvc_unregister_video(struct uvc_device *dev) +{ + if (dev->video.vdev) { + if (dev->video.vdev->minor == -1) + video_device_release(dev->video.vdev); + else + video_unregister_device(dev->video.vdev); + dev->video.vdev = NULL; + } +} + +/* + * Scan the UVC descriptors to locate a chain starting at an Output Terminal + * and containing the following units: + * + * - a USB Streaming Output Terminal + * - zero or one Processing Unit + * - zero, one or mode single-input Selector Units + * - zero or one multiple-input Selector Units, provided all inputs are + * connected to input terminals + * - zero, one or mode single-input Extension Units + * - one Camera Input Terminal, or one or more External terminals. + * + * A side forward scan is made on each detected entity to check for additional + * extension units. + */ +static int uvc_scan_chain_entity(struct uvc_video_device *video, + struct uvc_entity *entity) +{ + switch (UVC_ENTITY_TYPE(entity)) { + case VC_EXTENSION_UNIT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- XU %d", entity->id); + + if (entity->extension.bNrInPins != 1) { + uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more " + "than 1 input pin.\n", entity->id); + return -1; + } + + list_add_tail(&entity->chain, &video->extensions); + break; + + case VC_PROCESSING_UNIT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- PU %d", entity->id); + + if (video->processing != NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found multiple " + "Processing Units in chain.\n"); + return -1; + } + + video->processing = entity; + break; + + case VC_SELECTOR_UNIT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- SU %d", entity->id); + + /* Single-input selector units are ignored. */ + if (entity->selector.bNrInPins == 1) + break; + + if (video->selector != NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector " + "Units in chain.\n"); + return -1; + } + + video->selector = entity; + break; + + case ITT_VENDOR_SPECIFIC: + case ITT_CAMERA: + case ITT_MEDIA_TRANSPORT_INPUT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- IT %d\n", entity->id); + + list_add_tail(&entity->chain, &video->iterms); + break; + + default: + uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type " + "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity)); + return -1; + } + + return 0; +} + +static int uvc_scan_chain_forward(struct uvc_video_device *video, + struct uvc_entity *entity, struct uvc_entity *prev) +{ + struct uvc_entity *forward; + int found; + + /* Forward scan */ + forward = NULL; + found = 0; + + while (1) { + forward = uvc_entity_by_reference(video->dev, entity->id, + forward); + if (forward == NULL) + break; + + if (UVC_ENTITY_TYPE(forward) != VC_EXTENSION_UNIT || + forward == prev) + continue; + + if (forward->extension.bNrInPins != 1) { + uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has" + "more than 1 input pin.\n", entity->id); + return -1; + } + + list_add_tail(&forward->chain, &video->extensions); + if (uvc_trace_param & UVC_TRACE_PROBE) { + if (!found) + printk(" (-> XU"); + + printk(" %d", forward->id); + found = 1; + } + } + if (found) + printk(")"); + + return 0; +} + +static int uvc_scan_chain_backward(struct uvc_video_device *video, + struct uvc_entity *entity) +{ + struct uvc_entity *term; + int id = -1, i; + + switch (UVC_ENTITY_TYPE(entity)) { + case VC_EXTENSION_UNIT: + id = entity->extension.baSourceID[0]; + break; + + case VC_PROCESSING_UNIT: + id = entity->processing.bSourceID; + break; + + case VC_SELECTOR_UNIT: + /* Single-input selector units are ignored. */ + if (entity->selector.bNrInPins == 1) { + id = entity->selector.baSourceID[0]; + break; + } + + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- IT"); + + video->selector = entity; + for (i = 0; i < entity->selector.bNrInPins; ++i) { + id = entity->selector.baSourceID[i]; + term = uvc_entity_by_id(video->dev, id); + if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { + uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " + "input %d isn't connected to an " + "input terminal\n", entity->id, i); + return -1; + } + + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" %d", term->id); + + list_add_tail(&term->chain, &video->iterms); + uvc_scan_chain_forward(video, term, entity); + } + + if (uvc_trace_param & UVC_TRACE_PROBE) + printk("\n"); + + id = 0; + break; + } + + return id; +} + +static int uvc_scan_chain(struct uvc_video_device *video) +{ + struct uvc_entity *entity, *prev; + int id; + + entity = video->oterm; + uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id); + id = entity->output.bSourceID; + while (id != 0) { + prev = entity; + entity = uvc_entity_by_id(video->dev, id); + if (entity == NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found reference to " + "unknown entity %d.\n", id); + return -1; + } + + /* Process entity */ + if (uvc_scan_chain_entity(video, entity) < 0) + return -1; + + /* Forward scan */ + if (uvc_scan_chain_forward(video, entity, prev) < 0) + return -1; + + /* Stop when a terminal is found. */ + if (!UVC_ENTITY_IS_UNIT(entity)) + break; + + /* Backward scan */ + id = uvc_scan_chain_backward(video, entity); + if (id < 0) + return id; + } + + /* Initialize the video buffers queue. */ + uvc_queue_init(&video->queue); + + return 0; +} + +/* + * Register the video devices. + * + * The driver currently supports a single video device per control interface + * only. The terminal and units must match the following structure: + * + * ITT_CAMERA -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING + * + * The Extension Units, if present, must have a single input pin. The + * Processing Unit and Extension Units can be in any order. Additional + * Extension Units connected to the main chain as single-unit branches are + * also supported. + */ +static int uvc_register_video(struct uvc_device *dev) +{ + struct video_device *vdev; + struct uvc_entity *term; + int found = 0, ret; + + /* Check if the control interface matches the structure we expect. */ + list_for_each_entry(term, &dev->entities, list) { + struct uvc_streaming *streaming; + + if (UVC_ENTITY_TYPE(term) != TT_STREAMING) + continue; + + memset(&dev->video, 0, sizeof dev->video); + mutex_init(&dev->video.ctrl_mutex); + INIT_LIST_HEAD(&dev->video.iterms); + INIT_LIST_HEAD(&dev->video.extensions); + dev->video.oterm = term; + dev->video.dev = dev; + if (uvc_scan_chain(&dev->video) < 0) + continue; + + list_for_each_entry(streaming, &dev->streaming, list) { + if (streaming->header.bTerminalLink == term->id) { + dev->video.streaming = streaming; + found = 1; + break; + } + } + + if (found) + break; + } + + if (!found) { + uvc_printk(KERN_INFO, "No valid video chain found.\n"); + return -1; + } + + if (uvc_trace_param & UVC_TRACE_PROBE) { + uvc_printk(KERN_INFO, "Found a valid video chain ("); + list_for_each_entry(term, &dev->video.iterms, chain) { + printk("%d", term->id); + if (term->chain.next != &dev->video.iterms) + printk(","); + } + printk(" -> %d).\n", dev->video.oterm->id); + } + + /* Initialize the streaming interface with default streaming + * parameters. + */ + if ((ret = uvc_video_init(&dev->video)) < 0) { + uvc_printk(KERN_ERR, "Failed to initialize the device " + "(%d).\n", ret); + return ret; + } + + /* Register the device with V4L. */ + vdev = video_device_alloc(); + if (vdev == NULL) + return -1; + + /* We already hold a reference to dev->udev. The video device will be + * unregistered before the reference is released, so we don't need to + * get another one. + */ + vdev->dev = &dev->intf->dev; + vdev->type = 0; + vdev->type2 = 0; + vdev->minor = -1; + vdev->fops = &uvc_fops; + vdev->release = video_device_release; + strncpy(vdev->name, dev->name, sizeof vdev->name); + + /* Set the driver data before calling video_register_device, otherwise + * uvc_v4l2_open might race us. + * + * FIXME: usb_set_intfdata hasn't been called so far. Is that a + * problem ? Does any function which could be called here get + * a pointer to the usb_interface ? + */ + dev->video.vdev = vdev; + video_set_drvdata(vdev, &dev->video); + + if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) { + dev->video.vdev = NULL; + video_device_release(vdev); + return -1; + } + + return 0; +} + +/* + * Delete the UVC device. + * + * Called by the kernel when the last reference to the uvc_device structure + * is released. + * + * Unregistering the video devices is done here because every opened instance + * must be closed before the device can be unregistered. An alternative would + * have been to use another reference count for uvc_v4l2_open/uvc_release, and + * unregister the video devices on disconnect when that reference count drops + * to zero. + * + * As this function is called after or during disconnect(), all URBs have + * already been canceled by the USB core. There is no need to kill the + * interrupt URB manually. + */ +void uvc_delete(struct kref *kref) +{ + struct uvc_device *dev = container_of(kref, struct uvc_device, kref); + struct list_head *p, *n; + + /* Unregister the video device */ + uvc_unregister_video(dev); + usb_put_intf(dev->intf); + usb_put_dev(dev->udev); + + uvc_status_cleanup(dev); + uvc_ctrl_cleanup_device(dev); + + list_for_each_safe(p, n, &dev->entities) { + struct uvc_entity *entity; + entity = list_entry(p, struct uvc_entity, list); + kfree(entity); + } + + list_for_each_safe(p, n, &dev->streaming) { + struct uvc_streaming *streaming; + streaming = list_entry(p, struct uvc_streaming, list); + usb_driver_release_interface(&uvc_driver.driver, + streaming->intf); + usb_put_intf(streaming->intf); + kfree(streaming->format); + kfree(streaming->header.bmaControls); + kfree(streaming); + } + + kfree(dev); +} + +static int uvc_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct uvc_device *dev; + int ret; + + if (id->idVendor && id->idProduct) + uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s " + "(%04x:%04x)\n", udev->devpath, id->idVendor, + id->idProduct); + else + uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n", + udev->devpath); + + /* Allocate memory for the device and initialize it */ + if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL) + return -ENOMEM; + + INIT_LIST_HEAD(&dev->entities); + INIT_LIST_HEAD(&dev->streaming); + kref_init(&dev->kref); + + dev->udev = usb_get_dev(udev); + dev->intf = usb_get_intf(intf); + dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; + dev->quirks = id->driver_info | uvc_quirks_param; + + if (udev->product != NULL) + strncpy(dev->name, udev->product, sizeof dev->name); + else + snprintf(dev->name, sizeof dev->name, + "UVC Camera (%04x:%04x)", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + /* Parse the Video Class control descriptor */ + if (uvc_parse_control(dev) < 0) { + uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC " + "descriptors.\n"); + goto error; + } + + uvc_printk(KERN_INFO, "Found UVC %u.%02u device %s (%04x:%04x)\n", + dev->uvc_version >> 8, dev->uvc_version & 0xff, + udev->product ? udev->product : "", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + if (uvc_quirks_param != 0) { + uvc_printk(KERN_INFO, "Forcing device quirks 0x%x by module " + "parameter for testing purpose.\n", uvc_quirks_param); + uvc_printk(KERN_INFO, "Please report required quirks to the " + "linux-uvc-devel mailing list.\n"); + } + + /* Initialize controls */ + if (uvc_ctrl_init_device(dev) < 0) + goto error; + + /* Register the video devices */ + if (uvc_register_video(dev) < 0) + goto error; + + /* Save our data pointer in the interface data */ + usb_set_intfdata(intf, dev); + + /* Initialize the interrupt URB */ + if ((ret = uvc_status_init(dev)) < 0) { + uvc_printk(KERN_INFO, "Unable to initialize the status " + "endpoint (%d), status interrupt will not be " + "supported.\n", ret); + } + + uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n"); + return 0; + +error: + kref_put(&dev->kref, uvc_delete); + return -ENODEV; +} + +static void uvc_disconnect(struct usb_interface *intf) +{ + struct uvc_device *dev = usb_get_intfdata(intf); + + /* Set the USB interface data to NULL. This can be done outside the + * lock, as there's no other reader. + */ + usb_set_intfdata(intf, NULL); + + if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOSTREAMING) + return; + + /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide + * lock is needed to prevent uvc_disconnect from releasing its + * reference to the uvc_device instance after uvc_v4l2_open() received + * the pointer to the device (video_devdata) but before it got the + * chance to increase the reference count (kref_get). + */ + mutex_lock(&uvc_driver.open_mutex); + + dev->state |= UVC_DEV_DISCONNECTED; + kref_put(&dev->kref, uvc_delete); + + mutex_unlock(&uvc_driver.open_mutex); +} + +static int uvc_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct uvc_device *dev = usb_get_intfdata(intf); + + uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n", + intf->cur_altsetting->desc.bInterfaceNumber); + + /* Controls are cached on the fly so they don't need to be saved. */ + if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) + return uvc_status_suspend(dev); + + if (dev->video.streaming->intf != intf) { + uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB " + "interface mismatch.\n"); + return -EINVAL; + } + + return uvc_video_suspend(&dev->video); +} + +static int uvc_resume(struct usb_interface *intf) +{ + struct uvc_device *dev = usb_get_intfdata(intf); + int ret; + + uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", + intf->cur_altsetting->desc.bInterfaceNumber); + + if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) { + if ((ret = uvc_ctrl_resume_device(dev)) < 0) + return ret; + + return uvc_status_resume(dev); + } + + if (dev->video.streaming->intf != intf) { + uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB " + "interface mismatch.\n"); + return -EINVAL; + } + + return uvc_video_resume(&dev->video); +} + +/* ------------------------------------------------------------------------ + * Driver initialization and cleanup + */ + +/* + * The Logitech cameras listed below have their interface class set to + * VENDOR_SPEC because they don't announce themselves as UVC devices, even + * though they are compliant. + */ +static struct usb_device_id uvc_ids[] = { + /* ALi M5606 (Clevo M540SR) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0402, + .idProduct = 0x5606, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Creative Live! Optia */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x041e, + .idProduct = 0x4057, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Microsoft Lifecam NX-6000 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x045e, + .idProduct = 0x00f8, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Microsoft Lifecam VX-7000 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x045e, + .idProduct = 0x0723, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Logitech Quickcam Fusion */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c1, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam Orbit MP */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam Pro for Notebook */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c3, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam Pro 5000 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c5, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam OEM Dell Notebook */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c6, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Logitech Quickcam OEM Cisco VT Camera II */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x08c7, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0 }, + /* Apple Built-In iSight */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05ac, + .idProduct = 0x8501, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_BUILTIN_ISIGHT }, + /* Genesys Logic USB 2.0 PC Camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x05e3, + .idProduct = 0x0505, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Silicon Motion SM371 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x090c, + .idProduct = 0xb371, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* MT6227 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0e8d, + .idProduct = 0x0004, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Syntek (HP Spartan) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x5212, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Syntek (Asus U3S) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x8a33, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Ecamm Pico iMage */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x18cd, + .idProduct = 0xcafe, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, + /* Bodelin ProScopeHR */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_DEV_HI + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x19ab, + .idProduct = 0x1000, + .bcdDevice_hi = 0x0126, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STATUS_INTERVAL }, + /* SiGma Micro USB Web Camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x1c4f, + .idProduct = 0x3000, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_IGNORE_SELECTOR_UNIT}, + /* Acer OEM Webcam - Unknown vendor */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0100, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Packard Bell OEM Webcam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0101, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Acer Crystal Eye webcam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0102, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Acer OrbiCam - Unknown vendor */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0200, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Generic USB Video Class */ + { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, + {} +}; + +MODULE_DEVICE_TABLE(usb, uvc_ids); + +struct uvc_driver uvc_driver = { + .driver = { + .name = "uvcvideo", + .probe = uvc_probe, + .disconnect = uvc_disconnect, + .suspend = uvc_suspend, + .resume = uvc_resume, + .id_table = uvc_ids, + .supports_autosuspend = 1, + }, +}; + +static int __init uvc_init(void) +{ + int result; + + INIT_LIST_HEAD(&uvc_driver.devices); + INIT_LIST_HEAD(&uvc_driver.controls); + mutex_init(&uvc_driver.open_mutex); + mutex_init(&uvc_driver.ctrl_mutex); + + uvc_ctrl_init(); + + result = usb_register(&uvc_driver.driver); + if (result == 0) + printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n"); + return result; +} + +static void __exit uvc_cleanup(void) +{ + usb_deregister(&uvc_driver.driver); +} + +module_init(uvc_init); +module_exit(uvc_cleanup); + +module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(quirks, "Forced device quirks"); +module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(trace, "Trace level bitmask"); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/media/video/uvc/uvc_isight.c b/drivers/media/video/uvc/uvc_isight.c new file mode 100644 index 0000000..37bdefd --- /dev/null +++ b/drivers/media/video/uvc/uvc_isight.c @@ -0,0 +1,134 @@ +/* + * uvc_isight.c -- USB Video Class driver - iSight support + * + * Copyright (C) 2006-2007 + * Ivan N. Zlatev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include + +#include "uvcvideo.h" + +/* Built-in iSight webcams implements most of UVC 1.0 except a + * different packet format. Instead of sending a header at the + * beginning of each isochronous transfer payload, the webcam sends a + * single header per image (on its own in a packet), followed by + * packets containing data only. + * + * Offset Size (bytes) Description + * ------------------------------------------------------------------ + * 0x00 1 Header length + * 0x01 1 Flags (UVC-compliant) + * 0x02 4 Always equal to '11223344' + * 0x06 8 Always equal to 'deadbeefdeadface' + * 0x0e 16 Unknown + * + * The header can be prefixed by an optional, unknown-purpose byte. + */ + +static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, + const __u8 *data, unsigned int len) +{ + static const __u8 hdr[] = { + 0x11, 0x22, 0x33, 0x44, + 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xfa, 0xce + }; + + unsigned int maxlen, nbytes; + __u8 *mem; + int is_header = 0; + + if (buf == NULL) + return 0; + + if ((len >= 14 && memcmp(&data[2], hdr, 12) == 0) || + (len >= 15 && memcmp(&data[3], hdr, 12) == 0)) { + uvc_trace(UVC_TRACE_FRAME, "iSight header found\n"); + is_header = 1; + } + + /* Synchronize to the input stream by waiting for a header packet. */ + if (buf->state != UVC_BUF_STATE_ACTIVE) { + if (!is_header) { + uvc_trace(UVC_TRACE_FRAME, "Dropping packet (out of " + "sync).\n"); + return 0; + } + + buf->state = UVC_BUF_STATE_ACTIVE; + } + + /* Mark the buffer as done if we're at the beginning of a new frame. + * + * Empty buffers (bytesused == 0) don't trigger end of frame detection + * as it doesn't make sense to return an empty buffer. + */ + if (is_header && buf->buf.bytesused != 0) { + buf->state = UVC_BUF_STATE_DONE; + return -EAGAIN; + } + + /* Copy the video data to the buffer. Skip header packets, as they + * contain no data. + */ + if (!is_header) { + maxlen = buf->buf.length - buf->buf.bytesused; + mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused; + nbytes = min(len, maxlen); + memcpy(mem, data, nbytes); + buf->buf.bytesused += nbytes; + + if (len > maxlen || buf->buf.bytesused == buf->buf.length) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete " + "(overflow).\n"); + buf->state = UVC_BUF_STATE_DONE; + } + } + + return 0; +} + +void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, + struct uvc_buffer *buf) +{ + int ret, i; + + for (i = 0; i < urb->number_of_packets; ++i) { + if (urb->iso_frame_desc[i].status < 0) { + uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " + "lost (%d).\n", + urb->iso_frame_desc[i].status); + } + + /* Decode the payload packet. + * uvc_video_decode is entered twice when a frame transition + * has been detected because the end of frame can only be + * reliably detected when the first packet of the new frame + * is processed. The first pass detects the transition and + * closes the previous frame's buffer, the second pass + * processes the data of the first payload of the new frame. + */ + do { + ret = isight_decode(&video->queue, buf, + urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + + if (buf == NULL) + break; + + if (buf->state == UVC_BUF_STATE_DONE || + buf->state == UVC_BUF_STATE_ERROR) + buf = uvc_queue_next_buffer(&video->queue, buf); + } while (ret == -EAGAIN); + } +} diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c new file mode 100644 index 0000000..0923f0e --- /dev/null +++ b/drivers/media/video/uvc/uvc_queue.c @@ -0,0 +1,477 @@ +/* + * uvc_queue.c -- USB Video Class driver - Buffers management + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uvcvideo.h" + +/* ------------------------------------------------------------------------ + * Video buffers queue management. + * + * Video queues is initialized by uvc_queue_init(). The function performs + * basic initialization of the uvc_video_queue struct and never fails. + * + * Video buffer allocation and freeing are performed by uvc_alloc_buffers and + * uvc_free_buffers respectively. The former acquires the video queue lock, + * while the later must be called with the lock held (so that allocation can + * free previously allocated buffers). Trying to free buffers that are mapped + * to user space will return -EBUSY. + * + * Video buffers are managed using two queues. However, unlike most USB video + * drivers which use an in queue and an out queue, we use a main queue which + * holds all queued buffers (both 'empty' and 'done' buffers), and an irq + * queue which holds empty buffers. This design (copied from video-buf) + * minimizes locking in interrupt, as only one queue is shared between + * interrupt and user contexts. + * + * Use cases + * --------- + * + * Unless stated otherwise, all operations which modify the irq buffers queue + * are protected by the irq spinlock. + * + * 1. The user queues the buffers, starts streaming and dequeues a buffer. + * + * The buffers are added to the main and irq queues. Both operations are + * protected by the queue lock, and the latert is protected by the irq + * spinlock as well. + * + * The completion handler fetches a buffer from the irq queue and fills it + * with video data. If no buffer is available (irq queue empty), the handler + * returns immediately. + * + * When the buffer is full, the completion handler removes it from the irq + * queue, marks it as ready (UVC_BUF_STATE_DONE) and wake its wait queue. + * At that point, any process waiting on the buffer will be woken up. If a + * process tries to dequeue a buffer after it has been marked ready, the + * dequeing will succeed immediately. + * + * 2. Buffers are queued, user is waiting on a buffer and the device gets + * disconnected. + * + * When the device is disconnected, the kernel calls the completion handler + * with an appropriate status code. The handler marks all buffers in the + * irq queue as being erroneous (UVC_BUF_STATE_ERROR) and wakes them up so + * that any process waiting on a buffer gets woken up. + * + * Waking up up the first buffer on the irq list is not enough, as the + * process waiting on the buffer might restart the dequeue operation + * immediately. + * + */ + +void uvc_queue_init(struct uvc_video_queue *queue) +{ + mutex_init(&queue->mutex); + spin_lock_init(&queue->irqlock); + INIT_LIST_HEAD(&queue->mainqueue); + INIT_LIST_HEAD(&queue->irqqueue); +} + +/* + * Allocate the video buffers. + * + * Pages are reserved to make sure they will not be swaped, as they will be + * filled in URB completion handler. + * + * Buffers will be individually mapped, so they must all be page aligned. + */ +int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers, + unsigned int buflength) +{ + unsigned int bufsize = PAGE_ALIGN(buflength); + unsigned int i; + void *mem = NULL; + int ret; + + if (nbuffers > UVC_MAX_VIDEO_BUFFERS) + nbuffers = UVC_MAX_VIDEO_BUFFERS; + + mutex_lock(&queue->mutex); + + if ((ret = uvc_free_buffers(queue)) < 0) + goto done; + + /* Bail out if no buffers should be allocated. */ + if (nbuffers == 0) + goto done; + + /* Decrement the number of buffers until allocation succeeds. */ + for (; nbuffers > 0; --nbuffers) { + mem = vmalloc_32(nbuffers * bufsize); + if (mem != NULL) + break; + } + + if (mem == NULL) { + ret = -ENOMEM; + goto done; + } + + for (i = 0; i < nbuffers; ++i) { + memset(&queue->buffer[i], 0, sizeof queue->buffer[i]); + queue->buffer[i].buf.index = i; + queue->buffer[i].buf.m.offset = i * bufsize; + queue->buffer[i].buf.length = buflength; + queue->buffer[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + queue->buffer[i].buf.sequence = 0; + queue->buffer[i].buf.field = V4L2_FIELD_NONE; + queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP; + queue->buffer[i].buf.flags = 0; + init_waitqueue_head(&queue->buffer[i].wait); + } + + queue->mem = mem; + queue->count = nbuffers; + queue->buf_size = bufsize; + ret = nbuffers; + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +/* + * Free the video buffers. + * + * This function must be called with the queue lock held. + */ +int uvc_free_buffers(struct uvc_video_queue *queue) +{ + unsigned int i; + + for (i = 0; i < queue->count; ++i) { + if (queue->buffer[i].vma_use_count != 0) + return -EBUSY; + } + + if (queue->count) { + vfree(queue->mem); + queue->count = 0; + } + + return 0; +} + +static void __uvc_query_buffer(struct uvc_buffer *buf, + struct v4l2_buffer *v4l2_buf) +{ + memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf); + + if (buf->vma_use_count) + v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED; + + switch (buf->state) { + case UVC_BUF_STATE_ERROR: + case UVC_BUF_STATE_DONE: + v4l2_buf->flags |= V4L2_BUF_FLAG_DONE; + break; + case UVC_BUF_STATE_QUEUED: + case UVC_BUF_STATE_ACTIVE: + v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED; + break; + case UVC_BUF_STATE_IDLE: + default: + break; + } +} + +int uvc_query_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf) +{ + int ret = 0; + + mutex_lock(&queue->mutex); + if (v4l2_buf->index >= queue->count) { + ret = -EINVAL; + goto done; + } + + __uvc_query_buffer(&queue->buffer[v4l2_buf->index], v4l2_buf); + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +/* + * Queue a video buffer. Attempting to queue a buffer that has already been + * queued will return -EINVAL. + */ +int uvc_queue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf) +{ + struct uvc_buffer *buf; + unsigned long flags; + int ret = 0; + + uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index); + + if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + v4l2_buf->memory != V4L2_MEMORY_MMAP) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) " + "and/or memory (%u).\n", v4l2_buf->type, + v4l2_buf->memory); + return -EINVAL; + } + + mutex_lock(&queue->mutex); + if (v4l2_buf->index >= queue->count) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Out of range index.\n"); + ret = -EINVAL; + goto done; + } + + buf = &queue->buffer[v4l2_buf->index]; + if (buf->state != UVC_BUF_STATE_IDLE) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state " + "(%u).\n", buf->state); + ret = -EINVAL; + goto done; + } + + spin_lock_irqsave(&queue->irqlock, flags); + if (queue->flags & UVC_QUEUE_DISCONNECTED) { + spin_unlock_irqrestore(&queue->irqlock, flags); + ret = -ENODEV; + goto done; + } + buf->state = UVC_BUF_STATE_QUEUED; + buf->buf.bytesused = 0; + list_add_tail(&buf->stream, &queue->mainqueue); + list_add_tail(&buf->queue, &queue->irqqueue); + spin_unlock_irqrestore(&queue->irqlock, flags); + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +static int uvc_queue_waiton(struct uvc_buffer *buf, int nonblocking) +{ + if (nonblocking) { + return (buf->state != UVC_BUF_STATE_QUEUED && + buf->state != UVC_BUF_STATE_ACTIVE) + ? 0 : -EAGAIN; + } + + return wait_event_interruptible(buf->wait, + buf->state != UVC_BUF_STATE_QUEUED && + buf->state != UVC_BUF_STATE_ACTIVE); +} + +/* + * Dequeue a video buffer. If nonblocking is false, block until a buffer is + * available. + */ +int uvc_dequeue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf, int nonblocking) +{ + struct uvc_buffer *buf; + int ret = 0; + + if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + v4l2_buf->memory != V4L2_MEMORY_MMAP) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) " + "and/or memory (%u).\n", v4l2_buf->type, + v4l2_buf->memory); + return -EINVAL; + } + + mutex_lock(&queue->mutex); + if (list_empty(&queue->mainqueue)) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Empty buffer queue.\n"); + ret = -EINVAL; + goto done; + } + + buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream); + if ((ret = uvc_queue_waiton(buf, nonblocking)) < 0) + goto done; + + uvc_trace(UVC_TRACE_CAPTURE, "Dequeuing buffer %u (%u, %u bytes).\n", + buf->buf.index, buf->state, buf->buf.bytesused); + + switch (buf->state) { + case UVC_BUF_STATE_ERROR: + uvc_trace(UVC_TRACE_CAPTURE, "[W] Corrupted data " + "(transmission error).\n"); + ret = -EIO; + case UVC_BUF_STATE_DONE: + buf->state = UVC_BUF_STATE_IDLE; + break; + + case UVC_BUF_STATE_IDLE: + case UVC_BUF_STATE_QUEUED: + case UVC_BUF_STATE_ACTIVE: + default: + uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state %u " + "(driver bug?).\n", buf->state); + ret = -EINVAL; + goto done; + } + + list_del(&buf->stream); + __uvc_query_buffer(buf, v4l2_buf); + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +/* + * Poll the video queue. + * + * This function implements video queue polling and is intended to be used by + * the device poll handler. + */ +unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, + poll_table *wait) +{ + struct uvc_buffer *buf; + unsigned int mask = 0; + + mutex_lock(&queue->mutex); + if (list_empty(&queue->mainqueue)) { + mask |= POLLERR; + goto done; + } + buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream); + + poll_wait(file, &buf->wait, wait); + if (buf->state == UVC_BUF_STATE_DONE || + buf->state == UVC_BUF_STATE_ERROR) + mask |= POLLIN | POLLRDNORM; + +done: + mutex_unlock(&queue->mutex); + return mask; +} + +/* + * Enable or disable the video buffers queue. + * + * The queue must be enabled before starting video acquisition and must be + * disabled after stopping it. This ensures that the video buffers queue + * state can be properly initialized before buffers are accessed from the + * interrupt handler. + * + * Enabling the video queue initializes parameters (such as sequence number, + * sync pattern, ...). If the queue is already enabled, return -EBUSY. + * + * Disabling the video queue cancels the queue and removes all buffers from + * the main queue. + * + * This function can't be called from interrupt context. Use + * uvc_queue_cancel() instead. + */ +int uvc_queue_enable(struct uvc_video_queue *queue, int enable) +{ + unsigned int i; + int ret = 0; + + mutex_lock(&queue->mutex); + if (enable) { + if (uvc_queue_streaming(queue)) { + ret = -EBUSY; + goto done; + } + queue->sequence = 0; + queue->flags |= UVC_QUEUE_STREAMING; + } else { + uvc_queue_cancel(queue, 0); + INIT_LIST_HEAD(&queue->mainqueue); + + for (i = 0; i < queue->count; ++i) + queue->buffer[i].state = UVC_BUF_STATE_IDLE; + + queue->flags &= ~UVC_QUEUE_STREAMING; + } + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +/* + * Cancel the video buffers queue. + * + * Cancelling the queue marks all buffers on the irq queue as erroneous, + * wakes them up and remove them from the queue. + * + * If the disconnect parameter is set, further calls to uvc_queue_buffer will + * fail with -ENODEV. + * + * This function acquires the irq spinlock and can be called from interrupt + * context. + */ +void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) +{ + struct uvc_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&queue->irqlock, flags); + while (!list_empty(&queue->irqqueue)) { + buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + list_del(&buf->queue); + buf->state = UVC_BUF_STATE_ERROR; + wake_up(&buf->wait); + } + /* This must be protected by the irqlock spinlock to avoid race + * conditions between uvc_queue_buffer and the disconnection event that + * could result in an interruptible wait in uvc_dequeue_buffer. Do not + * blindly replace this logic by checking for the UVC_DEV_DISCONNECTED + * state outside the queue code. + */ + if (disconnect) + queue->flags |= UVC_QUEUE_DISCONNECTED; + spin_unlock_irqrestore(&queue->irqlock, flags); +} + +struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf) +{ + struct uvc_buffer *nextbuf; + unsigned long flags; + + if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) && + buf->buf.length != buf->buf.bytesused) { + buf->state = UVC_BUF_STATE_QUEUED; + buf->buf.bytesused = 0; + return buf; + } + + spin_lock_irqsave(&queue->irqlock, flags); + list_del(&buf->queue); + if (!list_empty(&queue->irqqueue)) + nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + else + nextbuf = NULL; + spin_unlock_irqrestore(&queue->irqlock, flags); + + buf->buf.sequence = queue->sequence++; + do_gettimeofday(&buf->buf.timestamp); + + wake_up(&buf->wait); + return nextbuf; +} diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c new file mode 100644 index 0000000..be9084e --- /dev/null +++ b/drivers/media/video/uvc/uvc_status.c @@ -0,0 +1,207 @@ +/* + * uvc_status.c -- USB Video Class driver - Status endpoint + * + * Copyright (C) 2007-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include + +#include "uvcvideo.h" + +/* -------------------------------------------------------------------------- + * Input device + */ +static int uvc_input_init(struct uvc_device *dev) +{ + struct usb_device *udev = dev->udev; + struct input_dev *input; + char *phys = NULL; + int ret; + + input = input_allocate_device(); + if (input == NULL) + return -ENOMEM; + + phys = kmalloc(6 + strlen(udev->bus->bus_name) + strlen(udev->devpath), + GFP_KERNEL); + if (phys == NULL) { + ret = -ENOMEM; + goto error; + } + sprintf(phys, "usb-%s-%s", udev->bus->bus_name, udev->devpath); + + input->name = dev->name; + input->phys = phys; + usb_to_input_id(udev, &input->id); + input->dev.parent = &dev->intf->dev; + + set_bit(EV_KEY, input->evbit); + set_bit(BTN_0, input->keybit); + + if ((ret = input_register_device(input)) < 0) + goto error; + + dev->input = input; + return 0; + +error: + input_free_device(input); + kfree(phys); + return ret; +} + +static void uvc_input_cleanup(struct uvc_device *dev) +{ + if (dev->input) + input_unregister_device(dev->input); +} + +/* -------------------------------------------------------------------------- + * Status interrupt endpoint + */ +static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len) +{ + if (len < 3) { + uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event " + "received.\n"); + return; + } + + if (data[2] == 0) { + if (len < 4) + return; + uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", + data[1], data[3] ? "pressed" : "released", len); + if (dev->input) + input_report_key(dev->input, BTN_0, data[3]); + } else { + uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x " + "len %d.\n", data[1], data[2], data[3], len); + } +} + +static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len) +{ + char *attrs[3] = { "value", "info", "failure" }; + + if (len < 6 || data[2] != 0 || data[4] > 2) { + uvc_trace(UVC_TRACE_STATUS, "Invalid control status event " + "received.\n"); + return; + } + + uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", + data[1], data[3], attrs[data[4]], len); +} + +static void uvc_status_complete(struct urb *urb) +{ + struct uvc_device *dev = urb->context; + int len, ret; + + switch (urb->status) { + case 0: + break; + + case -ENOENT: /* usb_kill_urb() called. */ + case -ECONNRESET: /* usb_unlink_urb() called. */ + case -ESHUTDOWN: /* The endpoint is being disabled. */ + case -EPROTO: /* Device is disconnected (reported by some + * host controller). */ + return; + + default: + uvc_printk(KERN_WARNING, "Non-zero status (%d) in status " + "completion handler.\n", urb->status); + return; + } + + len = urb->actual_length; + if (len > 0) { + switch (dev->status[0] & 0x0f) { + case UVC_STATUS_TYPE_CONTROL: + uvc_event_control(dev, dev->status, len); + break; + + case UVC_STATUS_TYPE_STREAMING: + uvc_event_streaming(dev, dev->status, len); + break; + + default: + uvc_printk(KERN_INFO, "unknown event type %u.\n", + dev->status[0]); + break; + } + } + + /* Resubmit the URB. */ + urb->interval = dev->int_ep->desc.bInterval; + if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n", + ret); + } +} + +int uvc_status_init(struct uvc_device *dev) +{ + struct usb_host_endpoint *ep = dev->int_ep; + unsigned int pipe; + int interval; + + if (ep == NULL) + return 0; + + uvc_input_init(dev); + + dev->int_urb = usb_alloc_urb(0, GFP_KERNEL); + if (dev->int_urb == NULL) + return -ENOMEM; + + pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress); + + /* For high-speed interrupt endpoints, the bInterval value is used as + * an exponent of two. Some developers forgot about it. + */ + interval = ep->desc.bInterval; + if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH && + (dev->quirks & UVC_QUIRK_STATUS_INTERVAL)) + interval = fls(interval) - 1; + + usb_fill_int_urb(dev->int_urb, dev->udev, pipe, + dev->status, sizeof dev->status, uvc_status_complete, + dev, interval); + + return usb_submit_urb(dev->int_urb, GFP_KERNEL); +} + +void uvc_status_cleanup(struct uvc_device *dev) +{ + usb_kill_urb(dev->int_urb); + usb_free_urb(dev->int_urb); + uvc_input_cleanup(dev); +} + +int uvc_status_suspend(struct uvc_device *dev) +{ + usb_kill_urb(dev->int_urb); + return 0; +} + +int uvc_status_resume(struct uvc_device *dev) +{ + if (dev->int_urb == NULL) + return 0; + + return usb_submit_urb(dev->int_urb, GFP_KERNEL); +} diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c new file mode 100644 index 0000000..2e0a665 --- /dev/null +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -0,0 +1,1105 @@ +/* + * uvc_v4l2.c -- USB Video Class driver - V4L2 API + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "uvcvideo.h" + +/* ------------------------------------------------------------------------ + * V4L2 interface + */ + +/* + * Mapping V4L2 controls to UVC controls can be straighforward if done well. + * Most of the UVC controls exist in V4L2, and can be mapped directly. Some + * must be grouped (for instance the Red Balance, Blue Balance and Do White + * Balance V4L2 controls use the White Balance Component UVC control) or + * otherwise translated. The approach we take here is to use a translation + * table for the controls which can be mapped directly, and handle the others + * manually. + */ +static int uvc_v4l2_query_menu(struct uvc_video_device *video, + struct v4l2_querymenu *query_menu) +{ + struct uvc_menu_info *menu_info; + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl; + + ctrl = uvc_find_control(video, query_menu->id, &mapping); + if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) + return -EINVAL; + + if (query_menu->index >= mapping->menu_count) + return -EINVAL; + + menu_info = &mapping->menu_info[query_menu->index]; + strncpy(query_menu->name, menu_info->name, 32); + return 0; +} + +/* + * Find the frame interval closest to the requested frame interval for the + * given frame format and size. This should be done by the device as part of + * the Video Probe and Commit negotiation, but some hardware don't implement + * that feature. + */ +static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval) +{ + unsigned int i; + + if (frame->bFrameIntervalType) { + __u32 best = -1, dist; + + for (i = 0; i < frame->bFrameIntervalType; ++i) { + dist = interval > frame->dwFrameInterval[i] + ? interval - frame->dwFrameInterval[i] + : frame->dwFrameInterval[i] - interval; + + if (dist > best) + break; + + best = dist; + } + + interval = frame->dwFrameInterval[i-1]; + } else { + const __u32 min = frame->dwFrameInterval[0]; + const __u32 max = frame->dwFrameInterval[1]; + const __u32 step = frame->dwFrameInterval[2]; + + interval = min + (interval - min + step/2) / step * step; + if (interval > max) + interval = max; + } + + return interval; +} + +static int uvc_v4l2_try_format(struct uvc_video_device *video, + struct v4l2_format *fmt, struct uvc_streaming_control *probe, + struct uvc_format **uvc_format, struct uvc_frame **uvc_frame) +{ + struct uvc_format *format = NULL; + struct uvc_frame *frame = NULL; + __u16 rw, rh; + unsigned int d, maxd; + unsigned int i; + __u32 interval; + int ret = 0; + __u8 *fcc; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + fcc = (__u8 *)&fmt->fmt.pix.pixelformat; + uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n", + fmt->fmt.pix.pixelformat, + fcc[0], fcc[1], fcc[2], fcc[3], + fmt->fmt.pix.width, fmt->fmt.pix.height); + + /* Check if the hardware supports the requested format. */ + for (i = 0; i < video->streaming->nformats; ++i) { + format = &video->streaming->format[i]; + if (format->fcc == fmt->fmt.pix.pixelformat) + break; + } + + if (format == NULL || format->fcc != fmt->fmt.pix.pixelformat) { + uvc_trace(UVC_TRACE_FORMAT, "Unsupported format 0x%08x.\n", + fmt->fmt.pix.pixelformat); + return -EINVAL; + } + + /* Find the closest image size. The distance between image sizes is + * the size in pixels of the non-overlapping regions between the + * requested size and the frame-specified size. + */ + rw = fmt->fmt.pix.width; + rh = fmt->fmt.pix.height; + maxd = (unsigned int)-1; + + for (i = 0; i < format->nframes; ++i) { + __u16 w = format->frame[i].wWidth; + __u16 h = format->frame[i].wHeight; + + d = min(w, rw) * min(h, rh); + d = w*h + rw*rh - 2*d; + if (d < maxd) { + maxd = d; + frame = &format->frame[i]; + } + + if (maxd == 0) + break; + } + + if (frame == NULL) { + uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n", + fmt->fmt.pix.width, fmt->fmt.pix.height); + return -EINVAL; + } + + /* Use the default frame interval. */ + interval = frame->dwDefaultFrameInterval; + uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us " + "(%u.%u fps).\n", interval/10, interval%10, 10000000/interval, + (100000000/interval)%10); + + /* Set the format index, frame index and frame interval. */ + memset(probe, 0, sizeof *probe); + probe->bmHint = 1; /* dwFrameInterval */ + probe->bFormatIndex = format->index; + probe->bFrameIndex = frame->bFrameIndex; + probe->dwFrameInterval = uvc_try_frame_interval(frame, interval); + /* Some webcams stall the probe control set request when the + * dwMaxVideoFrameSize field is set to zero. The UVC specification + * clearly states that the field is read-only from the host, so this + * is a webcam bug. Set dwMaxVideoFrameSize to the value reported by + * the webcam to work around the problem. + * + * The workaround could probably be enabled for all webcams, so the + * quirk can be removed if needed. It's currently useful to detect + * webcam bugs and fix them before they hit the market (providing + * developers test their webcams with the Linux driver as well as with + * the Windows driver). + */ + if (video->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS) + probe->dwMaxVideoFrameSize = + video->streaming->ctrl.dwMaxVideoFrameSize; + + /* Probe the device */ + if ((ret = uvc_probe_video(video, probe)) < 0) + goto done; + + fmt->fmt.pix.width = frame->wWidth; + fmt->fmt.pix.height = frame->wHeight; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; + fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize; + fmt->fmt.pix.colorspace = format->colorspace; + fmt->fmt.pix.priv = 0; + + if (uvc_format != NULL) + *uvc_format = format; + if (uvc_frame != NULL) + *uvc_frame = frame; + +done: + return ret; +} + +static int uvc_v4l2_get_format(struct uvc_video_device *video, + struct v4l2_format *fmt) +{ + struct uvc_format *format = video->streaming->cur_format; + struct uvc_frame *frame = video->streaming->cur_frame; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (format == NULL || frame == NULL) + return -EINVAL; + + fmt->fmt.pix.pixelformat = format->fcc; + fmt->fmt.pix.width = frame->wWidth; + fmt->fmt.pix.height = frame->wHeight; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; + fmt->fmt.pix.sizeimage = video->streaming->ctrl.dwMaxVideoFrameSize; + fmt->fmt.pix.colorspace = format->colorspace; + fmt->fmt.pix.priv = 0; + + return 0; +} + +static int uvc_v4l2_set_format(struct uvc_video_device *video, + struct v4l2_format *fmt) +{ + struct uvc_streaming_control probe; + struct uvc_format *format; + struct uvc_frame *frame; + int ret; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (uvc_queue_streaming(&video->queue)) + return -EBUSY; + + ret = uvc_v4l2_try_format(video, fmt, &probe, &format, &frame); + if (ret < 0) + return ret; + + if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) + return ret; + + memcpy(&video->streaming->ctrl, &probe, sizeof probe); + video->streaming->cur_format = format; + video->streaming->cur_frame = frame; + + return 0; +} + +static int uvc_v4l2_get_streamparm(struct uvc_video_device *video, + struct v4l2_streamparm *parm) +{ + uint32_t numerator, denominator; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + numerator = video->streaming->ctrl.dwFrameInterval; + denominator = 10000000; + uvc_simplify_fraction(&numerator, &denominator, 8, 333); + + memset(parm, 0, sizeof *parm); + parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.capturemode = 0; + parm->parm.capture.timeperframe.numerator = numerator; + parm->parm.capture.timeperframe.denominator = denominator; + parm->parm.capture.extendedmode = 0; + parm->parm.capture.readbuffers = 0; + + return 0; +} + +static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, + struct v4l2_streamparm *parm) +{ + struct uvc_frame *frame = video->streaming->cur_frame; + struct uvc_streaming_control probe; + uint32_t interval; + int ret; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (uvc_queue_streaming(&video->queue)) + return -EBUSY; + + memcpy(&probe, &video->streaming->ctrl, sizeof probe); + interval = uvc_fraction_to_interval( + parm->parm.capture.timeperframe.numerator, + parm->parm.capture.timeperframe.denominator); + + uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n", + parm->parm.capture.timeperframe.numerator, + parm->parm.capture.timeperframe.denominator, + interval); + probe.dwFrameInterval = uvc_try_frame_interval(frame, interval); + + /* Probe the device with the new settings. */ + if ((ret = uvc_probe_video(video, &probe)) < 0) + return ret; + + /* Commit the new settings. */ + if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) + return ret; + + memcpy(&video->streaming->ctrl, &probe, sizeof probe); + + /* Return the actual frame period. */ + parm->parm.capture.timeperframe.numerator = probe.dwFrameInterval; + parm->parm.capture.timeperframe.denominator = 10000000; + uvc_simplify_fraction(&parm->parm.capture.timeperframe.numerator, + &parm->parm.capture.timeperframe.denominator, + 8, 333); + + return 0; +} + +/* ------------------------------------------------------------------------ + * Privilege management + */ + +/* + * Privilege management is the multiple-open implementation basis. The current + * implementation is completely transparent for the end-user and doesn't + * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls. + * Those ioctls enable finer control on the device (by making possible for a + * user to request exclusive access to a device), but are not mature yet. + * Switching to the V4L2 priority mechanism might be considered in the future + * if this situation changes. + * + * Each open instance of a UVC device can either be in a privileged or + * unprivileged state. Only a single instance can be in a privileged state at + * a given time. Trying to perform an operation which requires privileges will + * automatically acquire the required privileges if possible, or return -EBUSY + * otherwise. Privileges are dismissed when closing the instance. + * + * Operations which require privileges are: + * + * - VIDIOC_S_INPUT + * - VIDIOC_S_PARM + * - VIDIOC_S_FMT + * - VIDIOC_TRY_FMT + * - VIDIOC_REQBUFS + */ +static int uvc_acquire_privileges(struct uvc_fh *handle) +{ + int ret = 0; + + /* Always succeed if the handle is already privileged. */ + if (handle->state == UVC_HANDLE_ACTIVE) + return 0; + + /* Check if the device already has a privileged handle. */ + mutex_lock(&uvc_driver.open_mutex); + if (atomic_inc_return(&handle->device->active) != 1) { + atomic_dec(&handle->device->active); + ret = -EBUSY; + goto done; + } + + handle->state = UVC_HANDLE_ACTIVE; + +done: + mutex_unlock(&uvc_driver.open_mutex); + return ret; +} + +static void uvc_dismiss_privileges(struct uvc_fh *handle) +{ + if (handle->state == UVC_HANDLE_ACTIVE) + atomic_dec(&handle->device->active); + + handle->state = UVC_HANDLE_PASSIVE; +} + +static int uvc_has_privileges(struct uvc_fh *handle) +{ + return handle->state == UVC_HANDLE_ACTIVE; +} + +/* ------------------------------------------------------------------------ + * V4L2 file operations + */ + +static int uvc_v4l2_open(struct inode *inode, struct file *file) +{ + struct video_device *vdev; + struct uvc_video_device *video; + struct uvc_fh *handle; + int ret = 0; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); + mutex_lock(&uvc_driver.open_mutex); + vdev = video_devdata(file); + video = video_get_drvdata(vdev); + + if (video->dev->state & UVC_DEV_DISCONNECTED) { + ret = -ENODEV; + goto done; + } + + ret = usb_autopm_get_interface(video->dev->intf); + if (ret < 0) + goto done; + + /* Create the device handle. */ + handle = kzalloc(sizeof *handle, GFP_KERNEL); + if (handle == NULL) { + usb_autopm_put_interface(video->dev->intf); + ret = -ENOMEM; + goto done; + } + + handle->device = video; + handle->state = UVC_HANDLE_PASSIVE; + file->private_data = handle; + + kref_get(&video->dev->kref); + +done: + mutex_unlock(&uvc_driver.open_mutex); + return ret; +} + +static int uvc_v4l2_release(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); + + /* Only free resources if this is a privileged handle. */ + if (uvc_has_privileges(handle)) { + uvc_video_enable(video, 0); + + mutex_lock(&video->queue.mutex); + if (uvc_free_buffers(&video->queue) < 0) + uvc_printk(KERN_ERR, "uvc_v4l2_release: Unable to " + "free buffers.\n"); + mutex_unlock(&video->queue.mutex); + } + + /* Release the file handle. */ + uvc_dismiss_privileges(handle); + kfree(handle); + file->private_data = NULL; + + usb_autopm_put_interface(video->dev->intf); + kref_put(&video->dev->kref, uvc_delete); + return 0; +} + +static int uvc_v4l2_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + int ret = 0; + + if (uvc_trace_param & UVC_TRACE_IOCTL) + v4l_printk_ioctl(cmd); + + switch (cmd) { + /* Query capabilities */ + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + memset(cap, 0, sizeof *cap); + strncpy(cap->driver, "uvcvideo", sizeof cap->driver); + strncpy(cap->card, vdev->name, 32); + strncpy(cap->bus_info, video->dev->udev->bus->bus_name, + sizeof cap->bus_info); + cap->version = DRIVER_VERSION_NUMBER; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_STREAMING; + break; + } + + /* Get, Set & Query control */ + case VIDIOC_QUERYCTRL: + return uvc_query_v4l2_ctrl(video, arg); + + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + struct v4l2_ext_control xctrl; + + memset(&xctrl, 0, sizeof xctrl); + xctrl.id = ctrl->id; + + uvc_ctrl_begin(video); + ret = uvc_ctrl_get(video, &xctrl); + uvc_ctrl_rollback(video); + if (ret >= 0) + ctrl->value = xctrl.value; + break; + } + + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + struct v4l2_ext_control xctrl; + + memset(&xctrl, 0, sizeof xctrl); + xctrl.id = ctrl->id; + xctrl.value = ctrl->value; + + uvc_ctrl_begin(video); + ret = uvc_ctrl_set(video, &xctrl); + if (ret < 0) { + uvc_ctrl_rollback(video); + return ret; + } + ret = uvc_ctrl_commit(video); + break; + } + + case VIDIOC_QUERYMENU: + return uvc_v4l2_query_menu(video, arg); + + case VIDIOC_G_EXT_CTRLS: + { + struct v4l2_ext_controls *ctrls = arg; + struct v4l2_ext_control *ctrl = ctrls->controls; + unsigned int i; + + uvc_ctrl_begin(video); + for (i = 0; i < ctrls->count; ++ctrl, ++i) { + ret = uvc_ctrl_get(video, ctrl); + if (ret < 0) { + uvc_ctrl_rollback(video); + ctrls->error_idx = i; + return ret; + } + } + ctrls->error_idx = 0; + ret = uvc_ctrl_rollback(video); + break; + } + + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + { + struct v4l2_ext_controls *ctrls = arg; + struct v4l2_ext_control *ctrl = ctrls->controls; + unsigned int i; + + ret = uvc_ctrl_begin(video); + if (ret < 0) + return ret; + + for (i = 0; i < ctrls->count; ++ctrl, ++i) { + ret = uvc_ctrl_set(video, ctrl); + if (ret < 0) { + uvc_ctrl_rollback(video); + ctrls->error_idx = i; + return ret; + } + } + + ctrls->error_idx = 0; + + if (cmd == VIDIOC_S_EXT_CTRLS) + ret = uvc_ctrl_commit(video); + else + ret = uvc_ctrl_rollback(video); + break; + } + + /* Get, Set & Enum input */ + case VIDIOC_ENUMINPUT: + { + const struct uvc_entity *selector = video->selector; + struct v4l2_input *input = arg; + struct uvc_entity *iterm = NULL; + u32 index = input->index; + int pin = 0; + + if (selector == NULL || + (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + if (index != 0) + return -EINVAL; + iterm = list_first_entry(&video->iterms, + struct uvc_entity, chain); + pin = iterm->id; + } else if (pin < selector->selector.bNrInPins) { + pin = selector->selector.baSourceID[index]; + list_for_each_entry(iterm, video->iterms.next, chain) { + if (iterm->id == pin) + break; + } + } + + if (iterm == NULL || iterm->id != pin) + return -EINVAL; + + memset(input, 0, sizeof *input); + input->index = index; + strncpy(input->name, iterm->name, sizeof input->name); + if (UVC_ENTITY_TYPE(iterm) == ITT_CAMERA) + input->type = V4L2_INPUT_TYPE_CAMERA; + break; + } + + case VIDIOC_G_INPUT: + { + u8 input; + + if (video->selector == NULL || + (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + *(int *)arg = 0; + break; + } + + ret = uvc_query_ctrl(video->dev, GET_CUR, video->selector->id, + video->dev->intfnum, SU_INPUT_SELECT_CONTROL, + &input, 1); + if (ret < 0) + return ret; + + *(int *)arg = input - 1; + break; + } + + case VIDIOC_S_INPUT: + { + u8 input = *(u32 *)arg + 1; + + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + if (video->selector == NULL || + (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + if (input != 1) + return -EINVAL; + break; + } + + if (input > video->selector->selector.bNrInPins) + return -EINVAL; + + return uvc_query_ctrl(video->dev, SET_CUR, video->selector->id, + video->dev->intfnum, SU_INPUT_SELECT_CONTROL, + &input, 1); + } + + /* Try, Get, Set & Enum format */ + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *fmt = arg; + struct uvc_format *format; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + fmt->index >= video->streaming->nformats) + return -EINVAL; + + format = &video->streaming->format[fmt->index]; + fmt->flags = 0; + if (format->flags & UVC_FMT_FLAG_COMPRESSED) + fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; + strncpy(fmt->description, format->name, + sizeof fmt->description); + fmt->description[sizeof fmt->description - 1] = 0; + fmt->pixelformat = format->fcc; + break; + } + + case VIDIOC_TRY_FMT: + { + struct uvc_streaming_control probe; + + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + return uvc_v4l2_try_format(video, arg, &probe, NULL, NULL); + } + + case VIDIOC_S_FMT: + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + return uvc_v4l2_set_format(video, arg); + + case VIDIOC_G_FMT: + return uvc_v4l2_get_format(video, arg); + + /* Frame size enumeration */ + case VIDIOC_ENUM_FRAMESIZES: + { + struct v4l2_frmsizeenum *fsize = arg; + struct uvc_format *format = NULL; + struct uvc_frame *frame; + int i; + + /* Look for the given pixel format */ + for (i = 0; i < video->streaming->nformats; i++) { + if (video->streaming->format[i].fcc == + fsize->pixel_format) { + format = &video->streaming->format[i]; + break; + } + } + if (format == NULL) + return -EINVAL; + + if (fsize->index >= format->nframes) + return -EINVAL; + + frame = &format->frame[fsize->index]; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = frame->wWidth; + fsize->discrete.height = frame->wHeight; + break; + } + + /* Frame interval enumeration */ + case VIDIOC_ENUM_FRAMEINTERVALS: + { + struct v4l2_frmivalenum *fival = arg; + struct uvc_format *format = NULL; + struct uvc_frame *frame = NULL; + int i; + + /* Look for the given pixel format and frame size */ + for (i = 0; i < video->streaming->nformats; i++) { + if (video->streaming->format[i].fcc == + fival->pixel_format) { + format = &video->streaming->format[i]; + break; + } + } + if (format == NULL) + return -EINVAL; + + for (i = 0; i < format->nframes; i++) { + if (format->frame[i].wWidth == fival->width && + format->frame[i].wHeight == fival->height) { + frame = &format->frame[i]; + break; + } + } + if (frame == NULL) + return -EINVAL; + + if (frame->bFrameIntervalType) { + if (fival->index >= frame->bFrameIntervalType) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = + frame->dwFrameInterval[fival->index]; + fival->discrete.denominator = 10000000; + uvc_simplify_fraction(&fival->discrete.numerator, + &fival->discrete.denominator, 8, 333); + } else { + fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; + fival->stepwise.min.numerator = + frame->dwFrameInterval[0]; + fival->stepwise.min.denominator = 10000000; + fival->stepwise.max.numerator = + frame->dwFrameInterval[1]; + fival->stepwise.max.denominator = 10000000; + fival->stepwise.step.numerator = + frame->dwFrameInterval[2]; + fival->stepwise.step.denominator = 10000000; + uvc_simplify_fraction(&fival->stepwise.min.numerator, + &fival->stepwise.min.denominator, 8, 333); + uvc_simplify_fraction(&fival->stepwise.max.numerator, + &fival->stepwise.max.denominator, 8, 333); + uvc_simplify_fraction(&fival->stepwise.step.numerator, + &fival->stepwise.step.denominator, 8, 333); + } + break; + } + + /* Get & Set streaming parameters */ + case VIDIOC_G_PARM: + return uvc_v4l2_get_streamparm(video, arg); + + case VIDIOC_S_PARM: + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + return uvc_v4l2_set_streamparm(video, arg); + + /* Cropping and scaling */ + case VIDIOC_CROPCAP: + { + struct v4l2_cropcap *ccap = arg; + struct uvc_frame *frame = video->streaming->cur_frame; + + if (ccap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + ccap->bounds.left = 0; + ccap->bounds.top = 0; + ccap->bounds.width = frame->wWidth; + ccap->bounds.height = frame->wHeight; + + ccap->defrect = ccap->bounds; + + ccap->pixelaspect.numerator = 1; + ccap->pixelaspect.denominator = 1; + break; + } + + case VIDIOC_G_CROP: + case VIDIOC_S_CROP: + return -EINVAL; + + /* Buffers & streaming */ + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *rb = arg; + unsigned int bufsize = + video->streaming->ctrl.dwMaxVideoFrameSize; + + if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + rb->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if ((ret = uvc_acquire_privileges(handle)) < 0) + return ret; + + ret = uvc_alloc_buffers(&video->queue, rb->count, bufsize); + if (ret < 0) + return ret; + + if (!(video->streaming->cur_format->flags & + UVC_FMT_FLAG_COMPRESSED)) + video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; + + rb->count = ret; + ret = 0; + break; + } + + case VIDIOC_QUERYBUF: + { + struct v4l2_buffer *buf = arg; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_query_buffer(&video->queue, buf); + } + + case VIDIOC_QBUF: + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_queue_buffer(&video->queue, arg); + + case VIDIOC_DQBUF: + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_dequeue_buffer(&video->queue, arg, + file->f_flags & O_NONBLOCK); + + case VIDIOC_STREAMON: + { + int *type = arg; + + if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!uvc_has_privileges(handle)) + return -EBUSY; + + if ((ret = uvc_video_enable(video, 1)) < 0) + return ret; + break; + } + + case VIDIOC_STREAMOFF: + { + int *type = arg; + + if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!uvc_has_privileges(handle)) + return -EBUSY; + + return uvc_video_enable(video, 0); + } + + /* Analog video standards make no sense for digital cameras. */ + case VIDIOC_ENUMSTD: + case VIDIOC_QUERYSTD: + case VIDIOC_G_STD: + case VIDIOC_S_STD: + + case VIDIOC_OVERLAY: + + case VIDIOC_ENUMAUDIO: + case VIDIOC_ENUMAUDOUT: + + case VIDIOC_ENUMOUTPUT: + uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd); + return -EINVAL; + + /* Dynamic controls. */ + case UVCIOC_CTRL_ADD: + { + struct uvc_xu_control_info *xinfo = arg; + struct uvc_control_info *info; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + info = kmalloc(sizeof *info, GFP_KERNEL); + if (info == NULL) + return -ENOMEM; + + memcpy(info->entity, xinfo->entity, sizeof info->entity); + info->index = xinfo->index; + info->selector = xinfo->selector; + info->size = xinfo->size; + info->flags = xinfo->flags; + + info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | + UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF; + + ret = uvc_ctrl_add_info(info); + if (ret < 0) + kfree(info); + break; + } + + case UVCIOC_CTRL_MAP: + { + struct uvc_xu_control_mapping *xmap = arg; + struct uvc_control_mapping *map; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + map = kmalloc(sizeof *map, GFP_KERNEL); + if (map == NULL) + return -ENOMEM; + + map->id = xmap->id; + memcpy(map->name, xmap->name, sizeof map->name); + memcpy(map->entity, xmap->entity, sizeof map->entity); + map->selector = xmap->selector; + map->size = xmap->size; + map->offset = xmap->offset; + map->v4l2_type = xmap->v4l2_type; + map->data_type = xmap->data_type; + + ret = uvc_ctrl_add_mapping(map); + if (ret < 0) + kfree(map); + break; + } + + case UVCIOC_CTRL_GET: + return uvc_xu_ctrl_query(video, arg, 0); + + case UVCIOC_CTRL_SET: + return uvc_xu_ctrl_query(video, arg, 1); + + default: + if ((ret = v4l_compat_translate_ioctl(inode, file, cmd, arg, + uvc_v4l2_do_ioctl)) == -ENOIOCTLCMD) + uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", + cmd); + return ret; + } + + return ret; +} + +static int uvc_v4l2_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_ioctl\n"); + return video_usercopy(inode, file, cmd, arg, uvc_v4l2_do_ioctl); +} + +static ssize_t uvc_v4l2_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n"); + return -ENODEV; +} + +/* + * VMA operations. + */ +static void uvc_vm_open(struct vm_area_struct *vma) +{ + struct uvc_buffer *buffer = vma->vm_private_data; + buffer->vma_use_count++; +} + +static void uvc_vm_close(struct vm_area_struct *vma) +{ + struct uvc_buffer *buffer = vma->vm_private_data; + buffer->vma_use_count--; +} + +static struct vm_operations_struct uvc_vm_ops = { + .open = uvc_vm_open, + .close = uvc_vm_close, +}; + +static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_buffer *buffer; + struct page *page; + unsigned long addr, start, size; + unsigned int i; + int ret = 0; + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n"); + + start = vma->vm_start; + size = vma->vm_end - vma->vm_start; + + mutex_lock(&video->queue.mutex); + + for (i = 0; i < video->queue.count; ++i) { + buffer = &video->queue.buffer[i]; + if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) + break; + } + + if (i == video->queue.count || size != video->queue.buf_size) { + ret = -EINVAL; + goto done; + } + + /* + * VM_IO marks the area as being an mmaped region for I/O to a + * device. It also prevents the region from being core dumped. + */ + vma->vm_flags |= VM_IO; + + addr = (unsigned long)video->queue.mem + buffer->buf.m.offset; + while (size > 0) { + page = vmalloc_to_page((void *)addr); + if ((ret = vm_insert_page(vma, start, page)) < 0) + goto done; + + start += PAGE_SIZE; + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &uvc_vm_ops; + vma->vm_private_data = buffer; + uvc_vm_open(vma); + +done: + mutex_unlock(&video->queue.mutex); + return ret; +} + +static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_video_device *video = video_get_drvdata(vdev); + + uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); + + return uvc_queue_poll(&video->queue, file, wait); +} + +struct file_operations uvc_fops = { + .owner = THIS_MODULE, + .open = uvc_v4l2_open, + .release = uvc_v4l2_release, + .ioctl = uvc_v4l2_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, + .read = uvc_v4l2_read, + .mmap = uvc_v4l2_mmap, + .poll = uvc_v4l2_poll, +}; diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c new file mode 100644 index 0000000..6faf1fb --- /dev/null +++ b/drivers/media/video/uvc/uvc_video.c @@ -0,0 +1,934 @@ +/* + * uvc_video.c -- USB Video Class driver - Video handling + * + * Copyright (C) 2005-2008 + * Laurent Pinchart (laurent.pinchart@skynet.be) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "uvcvideo.h" + +/* ------------------------------------------------------------------------ + * UVC Controls + */ + +static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size, + int timeout) +{ + __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE; + unsigned int pipe; + int ret; + + pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0) + : usb_sndctrlpipe(dev->udev, 0); + type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT; + + ret = usb_control_msg(dev->udev, pipe, query, type, cs << 8, + unit << 8 | intfnum, data, size, timeout); + + if (ret != size) { + uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u " + "(unit %u) : %d (exp. %u).\n", query, cs, unit, ret, + size); + return -EIO; + } + + return 0; +} + +int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size) +{ + return __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size, + UVC_CTRL_CONTROL_TIMEOUT); +} + +static void uvc_fixup_buffer_size(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl) +{ + struct uvc_format *format; + struct uvc_frame *frame; + + if (ctrl->bFormatIndex <= 0 || + ctrl->bFormatIndex > video->streaming->nformats) + return; + + format = &video->streaming->format[ctrl->bFormatIndex - 1]; + + if (ctrl->bFrameIndex <= 0 || + ctrl->bFrameIndex > format->nframes) + return; + + frame = &format->frame[ctrl->bFrameIndex - 1]; + + if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) || + (ctrl->dwMaxVideoFrameSize == 0 && + video->dev->uvc_version < 0x0110)) + ctrl->dwMaxVideoFrameSize = + frame->dwMaxVideoFrameBufferSize; +} + +static int uvc_get_video_ctrl(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl, int probe, __u8 query) +{ + __u8 data[34]; + __u8 size; + int ret; + + size = video->dev->uvc_version >= 0x0110 ? 34 : 26; + ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum, + probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, &data, size, + UVC_CTRL_STREAMING_TIMEOUT); + + if (ret < 0) + return ret; + + ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]); + ctrl->bFormatIndex = data[2]; + ctrl->bFrameIndex = data[3]; + ctrl->dwFrameInterval = le32_to_cpup((__le32 *)&data[4]); + ctrl->wKeyFrameRate = le16_to_cpup((__le16 *)&data[8]); + ctrl->wPFrameRate = le16_to_cpup((__le16 *)&data[10]); + ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]); + ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]); + ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]); + ctrl->dwMaxVideoFrameSize = + le32_to_cpu(get_unaligned((__le32 *)&data[18])); + ctrl->dwMaxPayloadTransferSize = + le32_to_cpu(get_unaligned((__le32 *)&data[22])); + + if (size == 34) { + ctrl->dwClockFrequency = + le32_to_cpu(get_unaligned((__le32 *)&data[26])); + ctrl->bmFramingInfo = data[30]; + ctrl->bPreferedVersion = data[31]; + ctrl->bMinVersion = data[32]; + ctrl->bMaxVersion = data[33]; + } else { + ctrl->dwClockFrequency = video->dev->clock_frequency; + ctrl->bmFramingInfo = 0; + ctrl->bPreferedVersion = 0; + ctrl->bMinVersion = 0; + ctrl->bMaxVersion = 0; + } + + /* Some broken devices return a null or wrong dwMaxVideoFrameSize. + * Try to get the value from the format and frame descriptor. + */ + uvc_fixup_buffer_size(video, ctrl); + + return 0; +} + +int uvc_set_video_ctrl(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl, int probe) +{ + __u8 data[34]; + __u8 size; + + size = video->dev->uvc_version >= 0x0110 ? 34 : 26; + memset(data, 0, sizeof data); + + *(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint); + data[2] = ctrl->bFormatIndex; + data[3] = ctrl->bFrameIndex; + *(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval); + *(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate); + *(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate); + *(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality); + *(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize); + *(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay); + /* Note: Some of the fields below are not required for IN devices (see + * UVC spec, 4.3.1.1), but we still copy them in case support for OUT + * devices is added in the future. */ + put_unaligned(cpu_to_le32(ctrl->dwMaxVideoFrameSize), + (__le32 *)&data[18]); + put_unaligned(cpu_to_le32(ctrl->dwMaxPayloadTransferSize), + (__le32 *)&data[22]); + + if (size == 34) { + put_unaligned(cpu_to_le32(ctrl->dwClockFrequency), + (__le32 *)&data[26]); + data[30] = ctrl->bmFramingInfo; + data[31] = ctrl->bPreferedVersion; + data[32] = ctrl->bMinVersion; + data[33] = ctrl->bMaxVersion; + } + + return __uvc_query_ctrl(video->dev, SET_CUR, 0, + video->streaming->intfnum, + probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, &data, size, + UVC_CTRL_STREAMING_TIMEOUT); +} + +int uvc_probe_video(struct uvc_video_device *video, + struct uvc_streaming_control *probe) +{ + struct uvc_streaming_control probe_min, probe_max; + __u16 bandwidth; + unsigned int i; + int ret; + + mutex_lock(&video->streaming->mutex); + + /* Perform probing. The device should adjust the requested values + * according to its capabilities. However, some devices, namely the + * first generation UVC Logitech webcams, don't implement the Video + * Probe control properly, and just return the needed bandwidth. For + * that reason, if the needed bandwidth exceeds the maximum available + * bandwidth, try to lower the quality. + */ + if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0) + goto done; + + /* Get the minimum and maximum values for compression settings. */ + if (!(video->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) { + ret = uvc_get_video_ctrl(video, &probe_min, 1, GET_MIN); + if (ret < 0) + goto done; + ret = uvc_get_video_ctrl(video, &probe_max, 1, GET_MAX); + if (ret < 0) + goto done; + + probe->wCompQuality = probe_max.wCompQuality; + } + + for (i = 0; i < 2; ++i) { + if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0 || + (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0) + goto done; + + if (video->streaming->intf->num_altsetting == 1) + break; + + bandwidth = probe->dwMaxPayloadTransferSize; + if (bandwidth <= video->streaming->maxpsize) + break; + + if (video->dev->quirks & UVC_QUIRK_PROBE_MINMAX) { + ret = -ENOSPC; + goto done; + } + + /* TODO: negotiate compression parameters */ + probe->wKeyFrameRate = probe_min.wKeyFrameRate; + probe->wPFrameRate = probe_min.wPFrameRate; + probe->wCompQuality = probe_max.wCompQuality; + probe->wCompWindowSize = probe_min.wCompWindowSize; + } + +done: + mutex_unlock(&video->streaming->mutex); + return ret; +} + +/* ------------------------------------------------------------------------ + * Video codecs + */ + +/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ +#define UVC_STREAM_EOH (1 << 7) +#define UVC_STREAM_ERR (1 << 6) +#define UVC_STREAM_STI (1 << 5) +#define UVC_STREAM_RES (1 << 4) +#define UVC_STREAM_SCR (1 << 3) +#define UVC_STREAM_PTS (1 << 2) +#define UVC_STREAM_EOF (1 << 1) +#define UVC_STREAM_FID (1 << 0) + +/* Video payload decoding is handled by uvc_video_decode_start(), + * uvc_video_decode_data() and uvc_video_decode_end(). + * + * uvc_video_decode_start is called with URB data at the start of a bulk or + * isochronous payload. It processes header data and returns the header size + * in bytes if successful. If an error occurs, it returns a negative error + * code. The following error codes have special meanings. + * + * - EAGAIN informs the caller that the current video buffer should be marked + * as done, and that the function should be called again with the same data + * and a new video buffer. This is used when end of frame conditions can be + * reliably detected at the beginning of the next frame only. + * + * If an error other than -EAGAIN is returned, the caller will drop the current + * payload. No call to uvc_video_decode_data and uvc_video_decode_end will be + * made until the next payload. -ENODATA can be used to drop the current + * payload if no other error code is appropriate. + * + * uvc_video_decode_data is called for every URB with URB data. It copies the + * data to the video buffer. + * + * uvc_video_decode_end is called with header data at the end of a bulk or + * isochronous payload. It performs any additional header data processing and + * returns 0 or a negative error code if an error occured. As header data have + * already been processed by uvc_video_decode_start, this functions isn't + * required to perform sanity checks a second time. + * + * For isochronous transfers where a payload is always transfered in a single + * URB, the three functions will be called in a row. + * + * To let the decoder process header data and update its internal state even + * when no video buffer is available, uvc_video_decode_start must be prepared + * to be called with a NULL buf parameter. uvc_video_decode_data and + * uvc_video_decode_end will never be called with a NULL buffer. + */ +static int uvc_video_decode_start(struct uvc_video_device *video, + struct uvc_buffer *buf, const __u8 *data, int len) +{ + __u8 fid; + + /* Sanity checks: + * - packet must be at least 2 bytes long + * - bHeaderLength value must be at least 2 bytes (see above) + * - bHeaderLength value can't be larger than the packet size. + */ + if (len < 2 || data[0] < 2 || data[0] > len) + return -EINVAL; + + /* Skip payloads marked with the error bit ("error frames"). */ + if (data[1] & UVC_STREAM_ERR) { + uvc_trace(UVC_TRACE_FRAME, "Dropping payload (error bit " + "set).\n"); + return -ENODATA; + } + + fid = data[1] & UVC_STREAM_FID; + + /* Store the payload FID bit and return immediately when the buffer is + * NULL. + */ + if (buf == NULL) { + video->last_fid = fid; + return -ENODATA; + } + + /* Synchronize to the input stream by waiting for the FID bit to be + * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE. + * queue->last_fid is initialized to -1, so the first isochronous + * frame will always be in sync. + * + * If the device doesn't toggle the FID bit, invert video->last_fid + * when the EOF bit is set to force synchronisation on the next packet. + */ + if (buf->state != UVC_BUF_STATE_ACTIVE) { + if (fid == video->last_fid) { + uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of " + "sync).\n"); + if ((video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) && + (data[1] & UVC_STREAM_EOF)) + video->last_fid ^= UVC_STREAM_FID; + return -ENODATA; + } + + /* TODO: Handle PTS and SCR. */ + buf->state = UVC_BUF_STATE_ACTIVE; + } + + /* Mark the buffer as done if we're at the beginning of a new frame. + * End of frame detection is better implemented by checking the EOF + * bit (FID bit toggling is delayed by one frame compared to the EOF + * bit), but some devices don't set the bit at end of frame (and the + * last payload can be lost anyway). We thus must check if the FID has + * been toggled. + * + * queue->last_fid is initialized to -1, so the first isochronous + * frame will never trigger an end of frame detection. + * + * Empty buffers (bytesused == 0) don't trigger end of frame detection + * as it doesn't make sense to return an empty buffer. This also + * avoids detecting and of frame conditions at FID toggling if the + * previous payload had the EOF bit set. + */ + if (fid != video->last_fid && buf->buf.bytesused != 0) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit " + "toggled).\n"); + buf->state = UVC_BUF_STATE_DONE; + return -EAGAIN; + } + + video->last_fid = fid; + + return data[0]; +} + +static void uvc_video_decode_data(struct uvc_video_device *video, + struct uvc_buffer *buf, const __u8 *data, int len) +{ + struct uvc_video_queue *queue = &video->queue; + unsigned int maxlen, nbytes; + void *mem; + + if (len <= 0) + return; + + /* Copy the video data to the buffer. */ + maxlen = buf->buf.length - buf->buf.bytesused; + mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused; + nbytes = min((unsigned int)len, maxlen); + memcpy(mem, data, nbytes); + buf->buf.bytesused += nbytes; + + /* Complete the current frame if the buffer size was exceeded. */ + if (len > maxlen) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n"); + buf->state = UVC_BUF_STATE_DONE; + } +} + +static void uvc_video_decode_end(struct uvc_video_device *video, + struct uvc_buffer *buf, const __u8 *data, int len) +{ + /* Mark the buffer as done if the EOF marker is set. */ + if (data[1] & UVC_STREAM_EOF && buf->buf.bytesused != 0) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n"); + if (data[0] == len) + uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n"); + buf->state = UVC_BUF_STATE_DONE; + if (video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) + video->last_fid ^= UVC_STREAM_FID; + } +} + +/* ------------------------------------------------------------------------ + * URB handling + */ + +/* + * Completion handler for video URBs. + */ +static void uvc_video_decode_isoc(struct urb *urb, + struct uvc_video_device *video, struct uvc_buffer *buf) +{ + u8 *mem; + int ret, i; + + for (i = 0; i < urb->number_of_packets; ++i) { + if (urb->iso_frame_desc[i].status < 0) { + uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " + "lost (%d).\n", urb->iso_frame_desc[i].status); + continue; + } + + /* Decode the payload header. */ + mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + do { + ret = uvc_video_decode_start(video, buf, mem, + urb->iso_frame_desc[i].actual_length); + if (ret == -EAGAIN) + buf = uvc_queue_next_buffer(&video->queue, buf); + } while (ret == -EAGAIN); + + if (ret < 0) + continue; + + /* Decode the payload data. */ + uvc_video_decode_data(video, buf, mem + ret, + urb->iso_frame_desc[i].actual_length - ret); + + /* Process the header again. */ + uvc_video_decode_end(video, buf, mem, ret); + + if (buf->state == UVC_BUF_STATE_DONE || + buf->state == UVC_BUF_STATE_ERROR) + buf = uvc_queue_next_buffer(&video->queue, buf); + } +} + +static void uvc_video_decode_bulk(struct urb *urb, + struct uvc_video_device *video, struct uvc_buffer *buf) +{ + u8 *mem; + int len, ret; + + mem = urb->transfer_buffer; + len = urb->actual_length; + video->bulk.payload_size += len; + + /* If the URB is the first of its payload, decode and save the + * header. + */ + if (video->bulk.header_size == 0) { + do { + ret = uvc_video_decode_start(video, buf, mem, len); + if (ret == -EAGAIN) + buf = uvc_queue_next_buffer(&video->queue, buf); + } while (ret == -EAGAIN); + + /* If an error occured skip the rest of the payload. */ + if (ret < 0 || buf == NULL) { + video->bulk.skip_payload = 1; + return; + } + + video->bulk.header_size = ret; + memcpy(video->bulk.header, mem, video->bulk.header_size); + + mem += ret; + len -= ret; + } + + /* The buffer queue might have been cancelled while a bulk transfer + * was in progress, so we can reach here with buf equal to NULL. Make + * sure buf is never dereferenced if NULL. + */ + + /* Process video data. */ + if (!video->bulk.skip_payload && buf != NULL) + uvc_video_decode_data(video, buf, mem, len); + + /* Detect the payload end by a URB smaller than the maximum size (or + * a payload size equal to the maximum) and process the header again. + */ + if (urb->actual_length < urb->transfer_buffer_length || + video->bulk.payload_size >= video->bulk.max_payload_size) { + if (!video->bulk.skip_payload && buf != NULL) { + uvc_video_decode_end(video, buf, video->bulk.header, + video->bulk.header_size); + if (buf->state == UVC_BUF_STATE_DONE || + buf->state == UVC_BUF_STATE_ERROR) + buf = uvc_queue_next_buffer(&video->queue, buf); + } + + video->bulk.header_size = 0; + video->bulk.skip_payload = 0; + video->bulk.payload_size = 0; + } +} + +static void uvc_video_complete(struct urb *urb) +{ + struct uvc_video_device *video = urb->context; + struct uvc_video_queue *queue = &video->queue; + struct uvc_buffer *buf = NULL; + unsigned long flags; + int ret; + + switch (urb->status) { + case 0: + break; + + default: + uvc_printk(KERN_WARNING, "Non-zero status (%d) in video " + "completion handler.\n", urb->status); + + case -ENOENT: /* usb_kill_urb() called. */ + if (video->frozen) + return; + + case -ECONNRESET: /* usb_unlink_urb() called. */ + case -ESHUTDOWN: /* The endpoint is being disabled. */ + uvc_queue_cancel(queue, urb->status == -ESHUTDOWN); + return; + } + + spin_lock_irqsave(&queue->irqlock, flags); + if (!list_empty(&queue->irqqueue)) + buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + spin_unlock_irqrestore(&queue->irqlock, flags); + + video->decode(urb, video, buf); + + if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n", + ret); + } +} + +/* + * Uninitialize isochronous/bulk URBs and free transfer buffers. + */ +static void uvc_uninit_video(struct uvc_video_device *video) +{ + struct urb *urb; + unsigned int i; + + for (i = 0; i < UVC_URBS; ++i) { + if ((urb = video->urb[i]) == NULL) + continue; + + usb_kill_urb(urb); + /* urb->transfer_buffer_length is not touched by USB core, so + * we can use it here as the buffer length. + */ + if (video->urb_buffer[i]) { + usb_buffer_free(video->dev->udev, + urb->transfer_buffer_length, + video->urb_buffer[i], urb->transfer_dma); + video->urb_buffer[i] = NULL; + } + + usb_free_urb(urb); + video->urb[i] = NULL; + } +} + +/* + * Initialize isochronous URBs and allocate transfer buffers. The packet size + * is given by the endpoint. + */ +static int uvc_init_video_isoc(struct uvc_video_device *video, + struct usb_host_endpoint *ep) +{ + struct urb *urb; + unsigned int npackets, i, j; + __u16 psize; + __u32 size; + + /* Compute the number of isochronous packets to allocate by dividing + * the maximum video frame size by the packet size. Limit the result + * to UVC_MAX_ISO_PACKETS. + */ + psize = le16_to_cpu(ep->desc.wMaxPacketSize); + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + + size = video->streaming->ctrl.dwMaxVideoFrameSize; + if (size > UVC_MAX_FRAME_SIZE) + return -EINVAL; + + npackets = (size + psize - 1) / psize; + if (npackets > UVC_MAX_ISO_PACKETS) + npackets = UVC_MAX_ISO_PACKETS; + + size = npackets * psize; + + for (i = 0; i < UVC_URBS; ++i) { + urb = usb_alloc_urb(npackets, GFP_KERNEL); + if (urb == NULL) { + uvc_uninit_video(video); + return -ENOMEM; + } + + video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev, + size, GFP_KERNEL, &urb->transfer_dma); + if (video->urb_buffer[i] == NULL) { + usb_free_urb(urb); + uvc_uninit_video(video); + return -ENOMEM; + } + + urb->dev = video->dev->udev; + urb->context = video; + urb->pipe = usb_rcvisocpipe(video->dev->udev, + ep->desc.bEndpointAddress); + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->interval = ep->desc.bInterval; + urb->transfer_buffer = video->urb_buffer[i]; + urb->complete = uvc_video_complete; + urb->number_of_packets = npackets; + urb->transfer_buffer_length = size; + + for (j = 0; j < npackets; ++j) { + urb->iso_frame_desc[j].offset = j * psize; + urb->iso_frame_desc[j].length = psize; + } + + video->urb[i] = urb; + } + + return 0; +} + +/* + * Initialize bulk URBs and allocate transfer buffers. The packet size is + * given by the endpoint. + */ +static int uvc_init_video_bulk(struct uvc_video_device *video, + struct usb_host_endpoint *ep) +{ + struct urb *urb; + unsigned int pipe, i; + __u16 psize; + __u32 size; + + /* Compute the bulk URB size. Some devices set the maximum payload + * size to a value too high for memory-constrained devices. We must + * then transfer the payload accross multiple URBs. To be consistant + * with isochronous mode, allocate maximum UVC_MAX_ISO_PACKETS per bulk + * URB. + */ + psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff; + size = video->streaming->ctrl.dwMaxPayloadTransferSize; + video->bulk.max_payload_size = size; + if (size > psize * UVC_MAX_ISO_PACKETS) + size = psize * UVC_MAX_ISO_PACKETS; + + pipe = usb_rcvbulkpipe(video->dev->udev, ep->desc.bEndpointAddress); + + for (i = 0; i < UVC_URBS; ++i) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (urb == NULL) { + uvc_uninit_video(video); + return -ENOMEM; + } + + video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev, + size, GFP_KERNEL, &urb->transfer_dma); + if (video->urb_buffer[i] == NULL) { + usb_free_urb(urb); + uvc_uninit_video(video); + return -ENOMEM; + } + + usb_fill_bulk_urb(urb, video->dev->udev, pipe, + video->urb_buffer[i], size, uvc_video_complete, + video); + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + + video->urb[i] = urb; + } + + return 0; +} + +/* + * Initialize isochronous/bulk URBs and allocate transfer buffers. + */ +static int uvc_init_video(struct uvc_video_device *video) +{ + struct usb_interface *intf = video->streaming->intf; + struct usb_host_interface *alts; + struct usb_host_endpoint *ep = NULL; + int intfnum = video->streaming->intfnum; + unsigned int bandwidth, psize, i; + int ret; + + video->last_fid = -1; + video->bulk.header_size = 0; + video->bulk.skip_payload = 0; + video->bulk.payload_size = 0; + + if (intf->num_altsetting > 1) { + /* Isochronous endpoint, select the alternate setting. */ + bandwidth = video->streaming->ctrl.dwMaxPayloadTransferSize; + + if (bandwidth == 0) { + uvc_printk(KERN_WARNING, "device %s requested null " + "bandwidth, defaulting to lowest.\n", + video->vdev->name); + bandwidth = 1; + } + + for (i = 0; i < intf->num_altsetting; ++i) { + alts = &intf->altsetting[i]; + ep = uvc_find_endpoint(alts, + video->streaming->header.bEndpointAddress); + if (ep == NULL) + continue; + + /* Check if the bandwidth is high enough. */ + psize = le16_to_cpu(ep->desc.wMaxPacketSize); + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + if (psize >= bandwidth) + break; + } + + if (i >= intf->num_altsetting) + return -EIO; + + if ((ret = usb_set_interface(video->dev->udev, intfnum, i)) < 0) + return ret; + + ret = uvc_init_video_isoc(video, ep); + } else { + /* Bulk endpoint, proceed to URB initialization. */ + ep = uvc_find_endpoint(&intf->altsetting[0], + video->streaming->header.bEndpointAddress); + if (ep == NULL) + return -EIO; + + ret = uvc_init_video_bulk(video, ep); + } + + if (ret < 0) + return ret; + + /* Submit the URBs. */ + for (i = 0; i < UVC_URBS; ++i) { + if ((ret = usb_submit_urb(video->urb[i], GFP_KERNEL)) < 0) { + uvc_printk(KERN_ERR, "Failed to submit URB %u " + "(%d).\n", i, ret); + uvc_uninit_video(video); + return ret; + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- + * Suspend/resume + */ + +/* + * Stop streaming without disabling the video queue. + * + * To let userspace applications resume without trouble, we must not touch the + * video buffers in any way. We mark the device as frozen to make sure the URB + * completion handler won't try to cancel the queue when we kill the URBs. + */ +int uvc_video_suspend(struct uvc_video_device *video) +{ + if (!uvc_queue_streaming(&video->queue)) + return 0; + + video->frozen = 1; + uvc_uninit_video(video); + usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); + return 0; +} + +/* + * Reconfigure the video interface and restart streaming if it was enable + * before suspend. + * + * If an error occurs, disable the video queue. This will wake all pending + * buffers, making sure userspace applications are notified of the problem + * instead of waiting forever. + */ +int uvc_video_resume(struct uvc_video_device *video) +{ + int ret; + + video->frozen = 0; + + if ((ret = uvc_set_video_ctrl(video, &video->streaming->ctrl, 0)) < 0) { + uvc_queue_enable(&video->queue, 0); + return ret; + } + + if (!uvc_queue_streaming(&video->queue)) + return 0; + + if ((ret = uvc_init_video(video)) < 0) + uvc_queue_enable(&video->queue, 0); + + return ret; +} + +/* ------------------------------------------------------------------------ + * Video device + */ + +/* + * Initialize the UVC video device by retrieving the default format and + * committing it. + * + * Some cameras (namely the Fuji Finepix) set the format and frame + * indexes to zero. The UVC standard doesn't clearly make this a spec + * violation, so try to silently fix the values if possible. + * + * This function is called before registering the device with V4L. + */ +int uvc_video_init(struct uvc_video_device *video) +{ + struct uvc_streaming_control *probe = &video->streaming->ctrl; + struct uvc_format *format = NULL; + struct uvc_frame *frame = NULL; + unsigned int i; + int ret; + + if (video->streaming->nformats == 0) { + uvc_printk(KERN_INFO, "No supported video formats found.\n"); + return -EINVAL; + } + + /* Alternate setting 0 should be the default, yet the XBox Live Vision + * Cam (and possibly other devices) crash or otherwise misbehave if + * they don't receive a SET_INTERFACE request before any other video + * control request. + */ + usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); + + /* Some webcams don't suport GET_DEF request on the probe control. We + * fall back to GET_CUR if GET_DEF fails. + */ + if ((ret = uvc_get_video_ctrl(video, probe, 1, GET_DEF)) < 0 && + (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0) + return ret; + + /* Check if the default format descriptor exists. Use the first + * available format otherwise. + */ + for (i = video->streaming->nformats; i > 0; --i) { + format = &video->streaming->format[i-1]; + if (format->index == probe->bFormatIndex) + break; + } + + if (format->nframes == 0) { + uvc_printk(KERN_INFO, "No frame descriptor found for the " + "default format.\n"); + return -EINVAL; + } + + /* Zero bFrameIndex might be correct. Stream-based formats (including + * MPEG-2 TS and DV) do not support frames but have a dummy frame + * descriptor with bFrameIndex set to zero. If the default frame + * descriptor is not found, use the first avalable frame. + */ + for (i = format->nframes; i > 0; --i) { + frame = &format->frame[i-1]; + if (frame->bFrameIndex == probe->bFrameIndex) + break; + } + + /* Commit the default settings. */ + probe->bFormatIndex = format->index; + probe->bFrameIndex = frame->bFrameIndex; + if ((ret = uvc_set_video_ctrl(video, probe, 0)) < 0) + return ret; + + video->streaming->cur_format = format; + video->streaming->cur_frame = frame; + atomic_set(&video->active, 0); + + /* Select the video decoding function */ + if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) + video->decode = uvc_video_decode_isight; + else if (video->streaming->intf->num_altsetting > 1) + video->decode = uvc_video_decode_isoc; + else + video->decode = uvc_video_decode_bulk; + + return 0; +} + +/* + * Enable or disable the video stream. + */ +int uvc_video_enable(struct uvc_video_device *video, int enable) +{ + int ret; + + if (!enable) { + uvc_uninit_video(video); + usb_set_interface(video->dev->udev, + video->streaming->intfnum, 0); + uvc_queue_enable(&video->queue, 0); + return 0; + } + + if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) + return ret; + + return uvc_init_video(video); +} diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h new file mode 100644 index 0000000..a995a78 --- /dev/null +++ b/drivers/media/video/uvc/uvcvideo.h @@ -0,0 +1,796 @@ +#ifndef _USB_VIDEO_H_ +#define _USB_VIDEO_H_ + +#include +#include + + +/* + * Dynamic controls + */ + +/* Data types for UVC control data */ +#define UVC_CTRL_DATA_TYPE_RAW 0 +#define UVC_CTRL_DATA_TYPE_SIGNED 1 +#define UVC_CTRL_DATA_TYPE_UNSIGNED 2 +#define UVC_CTRL_DATA_TYPE_BOOLEAN 3 +#define UVC_CTRL_DATA_TYPE_ENUM 4 +#define UVC_CTRL_DATA_TYPE_BITMASK 5 + +/* Control flags */ +#define UVC_CONTROL_SET_CUR (1 << 0) +#define UVC_CONTROL_GET_CUR (1 << 1) +#define UVC_CONTROL_GET_MIN (1 << 2) +#define UVC_CONTROL_GET_MAX (1 << 3) +#define UVC_CONTROL_GET_RES (1 << 4) +#define UVC_CONTROL_GET_DEF (1 << 5) +/* Control should be saved at suspend and restored at resume. */ +#define UVC_CONTROL_RESTORE (1 << 6) +/* Control can be updated by the camera. */ +#define UVC_CONTROL_AUTO_UPDATE (1 << 7) + +#define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \ + UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \ + UVC_CONTROL_GET_DEF) + +struct uvc_xu_control_info { + __u8 entity[16]; + __u8 index; + __u8 selector; + __u16 size; + __u32 flags; +}; + +struct uvc_xu_control_mapping { + __u32 id; + __u8 name[32]; + __u8 entity[16]; + __u8 selector; + + __u8 size; + __u8 offset; + enum v4l2_ctrl_type v4l2_type; + __u32 data_type; +}; + +struct uvc_xu_control { + __u8 unit; + __u8 selector; + __u16 size; + __u8 __user *data; +}; + +#define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info) +#define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping) +#define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) +#define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) + +#ifdef __KERNEL__ + +#include + +/* -------------------------------------------------------------------------- + * UVC constants + */ + +#define SC_UNDEFINED 0x00 +#define SC_VIDEOCONTROL 0x01 +#define SC_VIDEOSTREAMING 0x02 +#define SC_VIDEO_INTERFACE_COLLECTION 0x03 + +#define PC_PROTOCOL_UNDEFINED 0x00 + +#define CS_UNDEFINED 0x20 +#define CS_DEVICE 0x21 +#define CS_CONFIGURATION 0x22 +#define CS_STRING 0x23 +#define CS_INTERFACE 0x24 +#define CS_ENDPOINT 0x25 + +/* VideoControl class specific interface descriptor */ +#define VC_DESCRIPTOR_UNDEFINED 0x00 +#define VC_HEADER 0x01 +#define VC_INPUT_TERMINAL 0x02 +#define VC_OUTPUT_TERMINAL 0x03 +#define VC_SELECTOR_UNIT 0x04 +#define VC_PROCESSING_UNIT 0x05 +#define VC_EXTENSION_UNIT 0x06 + +/* VideoStreaming class specific interface descriptor */ +#define VS_UNDEFINED 0x00 +#define VS_INPUT_HEADER 0x01 +#define VS_OUTPUT_HEADER 0x02 +#define VS_STILL_IMAGE_FRAME 0x03 +#define VS_FORMAT_UNCOMPRESSED 0x04 +#define VS_FRAME_UNCOMPRESSED 0x05 +#define VS_FORMAT_MJPEG 0x06 +#define VS_FRAME_MJPEG 0x07 +#define VS_FORMAT_MPEG2TS 0x0a +#define VS_FORMAT_DV 0x0c +#define VS_COLORFORMAT 0x0d +#define VS_FORMAT_FRAME_BASED 0x10 +#define VS_FRAME_FRAME_BASED 0x11 +#define VS_FORMAT_STREAM_BASED 0x12 + +/* Endpoint type */ +#define EP_UNDEFINED 0x00 +#define EP_GENERAL 0x01 +#define EP_ENDPOINT 0x02 +#define EP_INTERRUPT 0x03 + +/* Request codes */ +#define RC_UNDEFINED 0x00 +#define SET_CUR 0x01 +#define GET_CUR 0x81 +#define GET_MIN 0x82 +#define GET_MAX 0x83 +#define GET_RES 0x84 +#define GET_LEN 0x85 +#define GET_INFO 0x86 +#define GET_DEF 0x87 + +/* VideoControl interface controls */ +#define VC_CONTROL_UNDEFINED 0x00 +#define VC_VIDEO_POWER_MODE_CONTROL 0x01 +#define VC_REQUEST_ERROR_CODE_CONTROL 0x02 + +/* Terminal controls */ +#define TE_CONTROL_UNDEFINED 0x00 + +/* Selector Unit controls */ +#define SU_CONTROL_UNDEFINED 0x00 +#define SU_INPUT_SELECT_CONTROL 0x01 + +/* Camera Terminal controls */ +#define CT_CONTROL_UNDEFINED 0x00 +#define CT_SCANNING_MODE_CONTROL 0x01 +#define CT_AE_MODE_CONTROL 0x02 +#define CT_AE_PRIORITY_CONTROL 0x03 +#define CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04 +#define CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05 +#define CT_FOCUS_ABSOLUTE_CONTROL 0x06 +#define CT_FOCUS_RELATIVE_CONTROL 0x07 +#define CT_FOCUS_AUTO_CONTROL 0x08 +#define CT_IRIS_ABSOLUTE_CONTROL 0x09 +#define CT_IRIS_RELATIVE_CONTROL 0x0a +#define CT_ZOOM_ABSOLUTE_CONTROL 0x0b +#define CT_ZOOM_RELATIVE_CONTROL 0x0c +#define CT_PANTILT_ABSOLUTE_CONTROL 0x0d +#define CT_PANTILT_RELATIVE_CONTROL 0x0e +#define CT_ROLL_ABSOLUTE_CONTROL 0x0f +#define CT_ROLL_RELATIVE_CONTROL 0x10 +#define CT_PRIVACY_CONTROL 0x11 + +/* Processing Unit controls */ +#define PU_CONTROL_UNDEFINED 0x00 +#define PU_BACKLIGHT_COMPENSATION_CONTROL 0x01 +#define PU_BRIGHTNESS_CONTROL 0x02 +#define PU_CONTRAST_CONTROL 0x03 +#define PU_GAIN_CONTROL 0x04 +#define PU_POWER_LINE_FREQUENCY_CONTROL 0x05 +#define PU_HUE_CONTROL 0x06 +#define PU_SATURATION_CONTROL 0x07 +#define PU_SHARPNESS_CONTROL 0x08 +#define PU_GAMMA_CONTROL 0x09 +#define PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0a +#define PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0b +#define PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0c +#define PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0d +#define PU_DIGITAL_MULTIPLIER_CONTROL 0x0e +#define PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0f +#define PU_HUE_AUTO_CONTROL 0x10 +#define PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11 +#define PU_ANALOG_LOCK_STATUS_CONTROL 0x12 + +#define LXU_MOTOR_PANTILT_RELATIVE_CONTROL 0x01 +#define LXU_MOTOR_PANTILT_RESET_CONTROL 0x02 +#define LXU_MOTOR_FOCUS_MOTOR_CONTROL 0x03 + +/* VideoStreaming interface controls */ +#define VS_CONTROL_UNDEFINED 0x00 +#define VS_PROBE_CONTROL 0x01 +#define VS_COMMIT_CONTROL 0x02 +#define VS_STILL_PROBE_CONTROL 0x03 +#define VS_STILL_COMMIT_CONTROL 0x04 +#define VS_STILL_IMAGE_TRIGGER_CONTROL 0x05 +#define VS_STREAM_ERROR_CODE_CONTROL 0x06 +#define VS_GENERATE_KEY_FRAME_CONTROL 0x07 +#define VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08 +#define VS_SYNC_DELAY_CONTROL 0x09 + +#define TT_VENDOR_SPECIFIC 0x0100 +#define TT_STREAMING 0x0101 + +/* Input Terminal types */ +#define ITT_VENDOR_SPECIFIC 0x0200 +#define ITT_CAMERA 0x0201 +#define ITT_MEDIA_TRANSPORT_INPUT 0x0202 + +/* Output Terminal types */ +#define OTT_VENDOR_SPECIFIC 0x0300 +#define OTT_DISPLAY 0x0301 +#define OTT_MEDIA_TRANSPORT_OUTPUT 0x0302 + +/* External Terminal types */ +#define EXTERNAL_VENDOR_SPECIFIC 0x0400 +#define COMPOSITE_CONNECTOR 0x0401 +#define SVIDEO_CONNECTOR 0x0402 +#define COMPONENT_CONNECTOR 0x0403 + +#define UVC_TERM_INPUT 0x0000 +#define UVC_TERM_OUTPUT 0x8000 + +#define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff) +#define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0) +#define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0) +#define UVC_ENTITY_IS_ITERM(entity) \ + (((entity)->type & 0x8000) == UVC_TERM_INPUT) +#define UVC_ENTITY_IS_OTERM(entity) \ + (((entity)->type & 0x8000) == UVC_TERM_OUTPUT) + +#define UVC_STATUS_TYPE_CONTROL 1 +#define UVC_STATUS_TYPE_STREAMING 2 + +/* ------------------------------------------------------------------------ + * GUIDs + */ +#define UVC_GUID_UVC_CAMERA \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} +#define UVC_GUID_UVC_OUTPUT \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02} +#define UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03} +#define UVC_GUID_UVC_PROCESSING \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01} +#define UVC_GUID_UVC_SELECTOR \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02} + +#define UVC_GUID_LOGITECH_DEV_INFO \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1e} +#define UVC_GUID_LOGITECH_USER_HW \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f} +#define UVC_GUID_LOGITECH_VIDEO \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x50} +#define UVC_GUID_LOGITECH_MOTOR \ + {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ + 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x56} + +#define UVC_GUID_FORMAT_MJPEG \ + { 'M', 'J', 'P', 'G', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_YUY2 \ + { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_NV12 \ + { 'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_YV12 \ + { 'Y', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_I420 \ + { 'I', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_UYVY \ + { 'U', 'Y', 'V', 'Y', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_Y800 \ + { 'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_BY8 \ + { 'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} + + +/* ------------------------------------------------------------------------ + * Driver specific constants. + */ + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0) + +/* Number of isochronous URBs. */ +#define UVC_URBS 5 +/* Maximum number of packets per isochronous URB. */ +#define UVC_MAX_ISO_PACKETS 40 +/* Maximum frame size in bytes, for sanity checking. */ +#define UVC_MAX_FRAME_SIZE (16*1024*1024) +/* Maximum number of video buffers. */ +#define UVC_MAX_VIDEO_BUFFERS 32 + +#define UVC_CTRL_CONTROL_TIMEOUT 300 +#define UVC_CTRL_STREAMING_TIMEOUT 1000 + +/* Devices quirks */ +#define UVC_QUIRK_STATUS_INTERVAL 0x00000001 +#define UVC_QUIRK_PROBE_MINMAX 0x00000002 +#define UVC_QUIRK_PROBE_EXTRAFIELDS 0x00000004 +#define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008 +#define UVC_QUIRK_STREAM_NO_FID 0x00000010 +#define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 + +/* Format flags */ +#define UVC_FMT_FLAG_COMPRESSED 0x00000001 +#define UVC_FMT_FLAG_STREAM 0x00000002 + +/* ------------------------------------------------------------------------ + * Structures. + */ + +struct uvc_device; + +/* TODO: Put the most frequently accessed fields at the beginning of + * structures to maximize cache efficiency. + */ +struct uvc_streaming_control { + __u16 bmHint; + __u8 bFormatIndex; + __u8 bFrameIndex; + __u32 dwFrameInterval; + __u16 wKeyFrameRate; + __u16 wPFrameRate; + __u16 wCompQuality; + __u16 wCompWindowSize; + __u16 wDelay; + __u32 dwMaxVideoFrameSize; + __u32 dwMaxPayloadTransferSize; + __u32 dwClockFrequency; + __u8 bmFramingInfo; + __u8 bPreferedVersion; + __u8 bMinVersion; + __u8 bMaxVersion; +}; + +struct uvc_menu_info { + __u32 value; + __u8 name[32]; +}; + +struct uvc_control_info { + struct list_head list; + struct list_head mappings; + + __u8 entity[16]; + __u8 index; + __u8 selector; + + __u16 size; + __u32 flags; +}; + +struct uvc_control_mapping { + struct list_head list; + + struct uvc_control_info *ctrl; + + __u32 id; + __u8 name[32]; + __u8 entity[16]; + __u8 selector; + + __u8 size; + __u8 offset; + enum v4l2_ctrl_type v4l2_type; + __u32 data_type; + + struct uvc_menu_info *menu_info; + __u32 menu_count; +}; + +struct uvc_control { + struct uvc_entity *entity; + struct uvc_control_info *info; + + __u8 index; /* Used to match the uvc_control entry with a + uvc_control_info. */ + __u8 dirty : 1, + loaded : 1, + modified : 1; + + __u8 *data; +}; + +struct uvc_format_desc { + char *name; + __u8 guid[16]; + __u32 fcc; +}; + +/* The term 'entity' refers to both UVC units and UVC terminals. + * + * The type field is either the terminal type (wTerminalType in the terminal + * descriptor), or the unit type (bDescriptorSubtype in the unit descriptor). + * As the bDescriptorSubtype field is one byte long, the type value will + * always have a null MSB for units. All terminal types defined by the UVC + * specification have a non-null MSB, so it is safe to use the MSB to + * differentiate between units and terminals as long as the descriptor parsing + * code makes sure terminal types have a non-null MSB. + * + * For terminals, the type's most significant bit stores the terminal + * direction (either UVC_TERM_INPUT or UVC_TERM_OUTPUT). The type field should + * always be accessed with the UVC_ENTITY_* macros and never directly. + */ + +struct uvc_entity { + struct list_head list; /* Entity as part of a UVC device. */ + struct list_head chain; /* Entity as part of a video device + * chain. */ + __u8 id; + __u16 type; + char name[64]; + + union { + struct { + __u16 wObjectiveFocalLengthMin; + __u16 wObjectiveFocalLengthMax; + __u16 wOcularFocalLength; + __u8 bControlSize; + __u8 *bmControls; + } camera; + + struct { + __u8 bControlSize; + __u8 *bmControls; + __u8 bTransportModeSize; + __u8 *bmTransportModes; + } media; + + struct { + __u8 bSourceID; + } output; + + struct { + __u8 bSourceID; + __u16 wMaxMultiplier; + __u8 bControlSize; + __u8 *bmControls; + __u8 bmVideoStandards; + } processing; + + struct { + __u8 bNrInPins; + __u8 *baSourceID; + } selector; + + struct { + __u8 guidExtensionCode[16]; + __u8 bNumControls; + __u8 bNrInPins; + __u8 *baSourceID; + __u8 bControlSize; + __u8 *bmControls; + __u8 *bmControlsType; + } extension; + }; + + unsigned int ncontrols; + struct uvc_control *controls; +}; + +struct uvc_frame { + __u8 bFrameIndex; + __u8 bmCapabilities; + __u16 wWidth; + __u16 wHeight; + __u32 dwMinBitRate; + __u32 dwMaxBitRate; + __u32 dwMaxVideoFrameBufferSize; + __u8 bFrameIntervalType; + __u32 dwDefaultFrameInterval; + __u32 *dwFrameInterval; +}; + +struct uvc_format { + __u8 type; + __u8 index; + __u8 bpp; + __u8 colorspace; + __u32 fcc; + __u32 flags; + + char name[32]; + + unsigned int nframes; + struct uvc_frame *frame; +}; + +struct uvc_streaming_header { + __u8 bNumFormats; + __u8 bEndpointAddress; + __u8 bTerminalLink; + __u8 bControlSize; + __u8 *bmaControls; + /* The following fields are used by input headers only. */ + __u8 bmInfo; + __u8 bStillCaptureMethod; + __u8 bTriggerSupport; + __u8 bTriggerUsage; +}; + +struct uvc_streaming { + struct list_head list; + + struct usb_interface *intf; + int intfnum; + __u16 maxpsize; + + struct uvc_streaming_header header; + + unsigned int nformats; + struct uvc_format *format; + + struct uvc_streaming_control ctrl; + struct uvc_format *cur_format; + struct uvc_frame *cur_frame; + + struct mutex mutex; +}; + +enum uvc_buffer_state { + UVC_BUF_STATE_IDLE = 0, + UVC_BUF_STATE_QUEUED = 1, + UVC_BUF_STATE_ACTIVE = 2, + UVC_BUF_STATE_DONE = 3, + UVC_BUF_STATE_ERROR = 4, +}; + +struct uvc_buffer { + unsigned long vma_use_count; + struct list_head stream; + + /* Touched by interrupt handler. */ + struct v4l2_buffer buf; + struct list_head queue; + wait_queue_head_t wait; + enum uvc_buffer_state state; +}; + +#define UVC_QUEUE_STREAMING (1 << 0) +#define UVC_QUEUE_DISCONNECTED (1 << 1) +#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2) + +struct uvc_video_queue { + void *mem; + unsigned int flags; + __u32 sequence; + + unsigned int count; + unsigned int buf_size; + struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS]; + struct mutex mutex; /* protects buffers and mainqueue */ + spinlock_t irqlock; /* protects irqqueue */ + + struct list_head mainqueue; + struct list_head irqqueue; +}; + +struct uvc_video_device { + struct uvc_device *dev; + struct video_device *vdev; + atomic_t active; + unsigned int frozen : 1; + + struct list_head iterms; + struct uvc_entity *oterm; + struct uvc_entity *processing; + struct uvc_entity *selector; + struct list_head extensions; + struct mutex ctrl_mutex; + + struct uvc_video_queue queue; + + /* Video streaming object, must always be non-NULL. */ + struct uvc_streaming *streaming; + + void (*decode) (struct urb *urb, struct uvc_video_device *video, + struct uvc_buffer *buf); + + /* Context data used by the bulk completion handler. */ + struct { + __u8 header[256]; + unsigned int header_size; + int skip_payload; + __u32 payload_size; + __u32 max_payload_size; + } bulk; + + struct urb *urb[UVC_URBS]; + char *urb_buffer[UVC_URBS]; + + __u8 last_fid; +}; + +enum uvc_device_state { + UVC_DEV_DISCONNECTED = 1, +}; + +struct uvc_device { + struct usb_device *udev; + struct usb_interface *intf; + __u32 quirks; + int intfnum; + char name[32]; + + enum uvc_device_state state; + struct kref kref; + struct list_head list; + + /* Video control interface */ + __u16 uvc_version; + __u32 clock_frequency; + + struct list_head entities; + + struct uvc_video_device video; + + /* Status Interrupt Endpoint */ + struct usb_host_endpoint *int_ep; + struct urb *int_urb; + __u8 status[16]; + struct input_dev *input; + + /* Video Streaming interfaces */ + struct list_head streaming; +}; + +enum uvc_handle_state { + UVC_HANDLE_PASSIVE = 0, + UVC_HANDLE_ACTIVE = 1, +}; + +struct uvc_fh { + struct uvc_video_device *device; + enum uvc_handle_state state; +}; + +struct uvc_driver { + struct usb_driver driver; + + struct mutex open_mutex; /* protects from open/disconnect race */ + + struct list_head devices; /* struct uvc_device list */ + struct list_head controls; /* struct uvc_control_info list */ + struct mutex ctrl_mutex; /* protects controls and devices + lists */ +}; + +/* ------------------------------------------------------------------------ + * Debugging, printing and logging + */ + +#define UVC_TRACE_PROBE (1 << 0) +#define UVC_TRACE_DESCR (1 << 1) +#define UVC_TRACE_CONTROL (1 << 2) +#define UVC_TRACE_FORMAT (1 << 3) +#define UVC_TRACE_CAPTURE (1 << 4) +#define UVC_TRACE_CALLS (1 << 5) +#define UVC_TRACE_IOCTL (1 << 6) +#define UVC_TRACE_FRAME (1 << 7) +#define UVC_TRACE_SUSPEND (1 << 8) +#define UVC_TRACE_STATUS (1 << 9) + +extern unsigned int uvc_trace_param; + +#define uvc_trace(flag, msg...) \ + do { \ + if (uvc_trace_param & flag) \ + printk(KERN_DEBUG "uvcvideo: " msg); \ + } while (0) + +#define uvc_printk(level, msg...) \ + printk(level "uvcvideo: " msg) + +#define UVC_GUID_FORMAT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" \ + "%02x%02x%02x%02x%02x%02x" +#define UVC_GUID_ARGS(guid) \ + (guid)[3], (guid)[2], (guid)[1], (guid)[0], \ + (guid)[5], (guid)[4], \ + (guid)[7], (guid)[6], \ + (guid)[8], (guid)[9], \ + (guid)[10], (guid)[11], (guid)[12], \ + (guid)[13], (guid)[14], (guid)[15] + +/* -------------------------------------------------------------------------- + * Internal functions. + */ + +/* Core driver */ +extern struct uvc_driver uvc_driver; +extern void uvc_delete(struct kref *kref); + +/* Video buffers queue management. */ +extern void uvc_queue_init(struct uvc_video_queue *queue); +extern int uvc_alloc_buffers(struct uvc_video_queue *queue, + unsigned int nbuffers, unsigned int buflength); +extern int uvc_free_buffers(struct uvc_video_queue *queue); +extern int uvc_query_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf); +extern int uvc_queue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf); +extern int uvc_dequeue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *v4l2_buf, int nonblocking); +extern int uvc_queue_enable(struct uvc_video_queue *queue, int enable); +extern void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect); +extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf); +extern unsigned int uvc_queue_poll(struct uvc_video_queue *queue, + struct file *file, poll_table *wait); +static inline int uvc_queue_streaming(struct uvc_video_queue *queue) +{ + return queue->flags & UVC_QUEUE_STREAMING; +} + +/* V4L2 interface */ +extern struct file_operations uvc_fops; + +/* Video */ +extern int uvc_video_init(struct uvc_video_device *video); +extern int uvc_video_suspend(struct uvc_video_device *video); +extern int uvc_video_resume(struct uvc_video_device *video); +extern int uvc_video_enable(struct uvc_video_device *video, int enable); +extern int uvc_probe_video(struct uvc_video_device *video, + struct uvc_streaming_control *probe); +extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size); +extern int uvc_set_video_ctrl(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl, int probe); + +/* Status */ +extern int uvc_status_init(struct uvc_device *dev); +extern void uvc_status_cleanup(struct uvc_device *dev); +extern int uvc_status_suspend(struct uvc_device *dev); +extern int uvc_status_resume(struct uvc_device *dev); + +/* Controls */ +extern struct uvc_control *uvc_find_control(struct uvc_video_device *video, + __u32 v4l2_id, struct uvc_control_mapping **mapping); +extern int uvc_query_v4l2_ctrl(struct uvc_video_device *video, + struct v4l2_queryctrl *v4l2_ctrl); + +extern int uvc_ctrl_add_info(struct uvc_control_info *info); +extern int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping); +extern int uvc_ctrl_init_device(struct uvc_device *dev); +extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); +extern int uvc_ctrl_resume_device(struct uvc_device *dev); +extern void uvc_ctrl_init(void); + +extern int uvc_ctrl_begin(struct uvc_video_device *video); +extern int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback); +static inline int uvc_ctrl_commit(struct uvc_video_device *video) +{ + return __uvc_ctrl_commit(video, 0); +} +static inline int uvc_ctrl_rollback(struct uvc_video_device *video) +{ + return __uvc_ctrl_commit(video, 1); +} + +extern int uvc_ctrl_get(struct uvc_video_device *video, + struct v4l2_ext_control *xctrl); +extern int uvc_ctrl_set(struct uvc_video_device *video, + struct v4l2_ext_control *xctrl); + +extern int uvc_xu_ctrl_query(struct uvc_video_device *video, + struct uvc_xu_control *ctrl, int set); + +/* Utility functions */ +extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, + unsigned int n_terms, unsigned int threshold); +extern uint32_t uvc_fraction_to_interval(uint32_t numerator, + uint32_t denominator); +extern struct usb_host_endpoint *uvc_find_endpoint( + struct usb_host_interface *alts, __u8 epaddr); + +/* Quirks support */ +void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, + struct uvc_buffer *buf); + +#endif /* __KERNEL__ */ + +#endif -- cgit v0.10.2 From dd4f0888f8b42a97c93a66617a4f9acaff3089d6 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 30 Jun 2008 15:03:14 -0700 Subject: [IA64] Bugfix for system with 32 cpus On a system where there are no hot pluggable cpus "additional_cpus" is still set to -1 at the point where we call per_cpu_scan_finalize(). If we didn't find an SRAT table and so pick the default "32" for the number of cpus, when we get to: high_cpu = min(high_cpu + reserve_cpus, NR_CPUS); we will end up initializing for just 31 cpus ... and so we will die horribly when bringing up cpu#32. Problem introduced by: 2c6e6db41f01b6b4eb98809350827c9678996698 "Minimize per_cpu reservations." Acked-by: Robin Holt Signed-off-by: Tony Luck diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c index 4ae15c8..632cda8 100644 --- a/arch/ia64/kernel/setup.c +++ b/arch/ia64/kernel/setup.c @@ -547,7 +547,8 @@ setup_arch (char **cmdline_p) # ifdef CONFIG_ACPI_NUMA acpi_numa_init(); per_cpu_scan_finalize((cpus_weight(early_cpu_possible_map) == 0 ? - 32 : cpus_weight(early_cpu_possible_map)), additional_cpus); + 32 : cpus_weight(early_cpu_possible_map)), + additional_cpus > 0 ? additional_cpus : 0); # endif #else # ifdef CONFIG_SMP -- cgit v0.10.2 From 3a677d216445dba3332a000063405de3fc135859 Mon Sep 17 00:00:00 2001 From: Doug Chapman Date: Mon, 30 Jun 2008 15:06:48 -0700 Subject: [IA64] export account_system_vtime The symbol account_system_vtime is used by the kvm module but not exported. This breaks building with CONFIG_VIRT_CPU_ACCOUNTING and CONFIG_KVM=m. Signed-off-by: Doug Chapman Acked-by: Hidetosho Seto Signed-off-by: Tony Luck diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c index 8c73643..aad1b7b 100644 --- a/arch/ia64/kernel/time.c +++ b/arch/ia64/kernel/time.c @@ -117,6 +117,7 @@ void account_system_vtime(struct task_struct *tsk) local_irq_restore(flags); } +EXPORT_SYMBOL_GPL(account_system_vtime); /* * Called from the timer interrupt handler to charge accumulated user time -- cgit v0.10.2 From 619b0488038224391e64fa03854651ca0f5efe56 Mon Sep 17 00:00:00 2001 From: Raistlin Date: Thu, 26 Jun 2008 18:54:09 +0200 Subject: sched: fix divide error when trying to configure rt_period to zero Here it is another little Oops we found while configuring invalid values via cgroups: echo 0 > /dev/cgroups/0/cpu.rt_period_us or echo 4294967296 > /dev/cgroups/0/cpu.rt_period_us [ 205.509825] divide error: 0000 [#1] [ 205.510151] Modules linked in: [ 205.510151] [ 205.510151] Pid: 2339, comm: bash Not tainted (2.6.26-rc8 #33) [ 205.510151] EIP: 0060:[] EFLAGS: 00000293 CPU: 0 [ 205.510151] EIP is at div64_u64+0x5f/0x70 [ 205.510151] EAX: 0000389f EBX: 00000000 ECX: 00000000 EDX: 00000000 [ 205.510151] ESI: d9800000 EDI: 00000000 EBP: c6cede60 ESP: c6cede50 [ 205.510151] DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 0068 [ 205.510151] Process bash (pid: 2339, ti=c6cec000 task=c79be370 task.ti=c6cec000) [ 205.510151] Stack: d9800000 0000389f c05971a0 d9800000 c6cedeb4 c0214dbd 00000000 00000000 [ 205.510151] c6cede88 c0242bd8 c05377c0 c7a41b40 00000000 00000000 00000000 c05971a0 [ 205.510151] c780ed20 c7508494 c7a41b40 00000000 00000002 c6cedebc c05971a0 ffffffea [ 205.510151] Call Trace: [ 205.510151] [] ? __rt_schedulable+0x1cd/0x240 [ 205.510151] [] ? cgroup_file_open+0x18/0xe0 [ 205.510151] [] ? tg_set_bandwidth+0xa4/0xf0 [ 205.510151] [] ? sched_group_set_rt_period+0x36/0x50 [ 205.510151] [] ? cpu_rt_period_write_uint+0xe/0x10 [ 205.510151] [] ? cgroup_file_write+0x125/0x160 [ 205.510151] [] ? hrtimer_interrupt+0x155/0x190 [ 205.510151] [] ? security_file_permission+0xf/0x20 [ 205.510151] [] ? rw_verify_area+0x48/0xc0 [ 205.510151] [] ? dupfd+0x104/0x130 [ 205.510151] [] ? vfs_write+0x9c/0x160 [ 205.510151] [] ? cgroup_file_write+0x0/0x160 [ 205.510151] [] ? sys_write+0x3d/0x70 [ 205.510151] [] ? sysenter_past_esp+0x6a/0x91 [ 205.510151] ======================= [ 205.510151] Code: 0f 45 de 31 f6 0f ad d0 d3 ea f6 c1 20 0f 45 c2 0f 45 d6 89 45 f0 89 55 f4 8b 55 f4 31 c9 8b 45 f0 39 d3 89 c6 77 08 89 d0 31 d2 f3 89 c1 83 c4 08 89 f0 f7 f3 89 ca 5b 5e 5d c3 55 89 e5 56 [ 205.510151] EIP: [] div64_u64+0x5f/0x70 SS:ESP 0068:c6cede50 The attached patch solves the issue for me. I'm checking as soon as possible for the period not being zero since, if it is, going ahead is useless. This way we also save a mutex_lock() and a read_lock() wrt doing it inside tg_set_bandwidth() or __rt_schedulable(). Signed-off-by: Dario Faggioli Signed-off-by: Michael Trimarchi Signed-off-by: Ingo Molnar diff --git a/kernel/sched.c b/kernel/sched.c index a66e856..94ead43 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -8502,6 +8502,9 @@ int sched_group_set_rt_period(struct task_group *tg, long rt_period_us) rt_period = (u64)rt_period_us * NSEC_PER_USEC; rt_runtime = tg->rt_bandwidth.rt_runtime; + if (rt_period == 0) + return -EINVAL; + return tg_set_bandwidth(tg, rt_period, rt_runtime); } -- cgit v0.10.2 From efac41894df57d32b483ac622d03541b5b2692c0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 1 Jul 2008 08:56:32 +0200 Subject: x86: fix NODES_SHIFT Kconfig range commit 4323838215184f5a2f081e0d17b8d60731b03164 x86: change size of node ids from u8 to s16 set the range for NODES_SHIFT to 1..15. The possible range is 1..9 Fixes Bugzilla #10726 Reported-by: Dave Jones Signed-off-by: Thomas Gleixner diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 52e18e6..8a07f41 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -961,8 +961,8 @@ config NUMA_EMU number of nodes. This is only useful for debugging. config NODES_SHIFT - int "Max num nodes shift(1-15)" - range 1 15 if X86_64 + int "Max num nodes shift(1-9)" + range 1 9 if X86_64 default "6" if X86_64 default "4" if X86_NUMAQ default "3" -- cgit v0.10.2 From d585d0b9d73ed999cc7b8cf3cac4a5b01abb544e Mon Sep 17 00:00:00 2001 From: Divyesh Shah Date: Mon, 16 Jun 2008 18:37:08 +0200 Subject: block: Fix the starving writes bug in the anticipatory IO scheduler AS scheduler alternates between issuing read and write batches. It does the batch switch only after all requests from the previous batch are completed. When switching to a write batch, if there is an on-going read request, it waits for its completion and indicates its intention of switching by setting ad->changed_batch and the new direction but does not update the batch_expire_time for the new write batch which it does in the case of no previous pending requests. On completion of the read request, it sees that we were waiting for the switch and schedules work for kblockd right away and resets the ad->changed_data flag. Now when kblockd enters dispatch_request where it is expected to pick up a write request, it in turn ends the write batch because the batch_expire_timer was not updated and shows the expire timestamp for the previous batch. This results in the write starvation for all the cases where there is the intention for switching to a write batch, but there is a previous in-flight read request and the batch gets reverted to a read_batch right away. This also holds true in the reverse case (switching from a write batch to a read batch with an in-flight write request). I've checked that this bug exists on 2.6.11, 2.6.18, 2.6.24 and linux-2.6-block git HEAD. I've tested the fix on x86 platforms with SCSI drives where the driver asks for the next request while a current request is in-flight. This patch is based off linux-2.6-block git HEAD. Bug reproduction: A simple scenario which reproduces this bug is: - dd if=/dev/hda3 of=/dev/null & - lilo The lilo takes forever to complete. This can also be reproduced fairly easily with the earlier dd and another test program doing msync(). The example test program below should print out a message after every iteration but it simply hangs forever. With this bugfix it makes forward progress. ==== Example test program using msync() (thanks to suleiman AT google DOT com) inline uint64_t rdtsc(void) { int64_t tsc; __asm __volatile("rdtsc" : "=A" (tsc)); return (tsc); } int main(int argc, char **argv) { struct stat st; uint64_t e, s, t; char *p, q; long i; int fd; if (argc < 2) { printf("Usage: %s \n", argv[0]); return (1); } if ((fd = open(argv[1], O_RDWR | O_NOATIME)) < 0) err(1, "open"); if (fstat(fd, &st) < 0) err(1, "fstat"); p = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); t = 0; for (i = 0; i < 1000; i++) { *p = 0; msync(p, 4096, MS_SYNC); s = rdtsc(); *p = 0; __asm __volatile(""::: "memory"); e = rdtsc(); if (argc > 2) printf("%d: %lld cycles %jd %jd\n", i, e - s, (intmax_t)s, (intmax_t)e); t += e - s; } printf("average time: %lld cycles\n", t / 1000); return (0); } Cc: Acked-by: Nick Piggin Signed-off-by: Jens Axboe diff --git a/block/as-iosched.c b/block/as-iosched.c index 8c39467..743f33a 100644 --- a/block/as-iosched.c +++ b/block/as-iosched.c @@ -831,6 +831,8 @@ static void as_completed_request(struct request_queue *q, struct request *rq) } if (ad->changed_batch && ad->nr_dispatched == 1) { + ad->current_batch_expires = jiffies + + ad->batch_expire[ad->batch_data_dir]; kblockd_schedule_work(&ad->antic_work); ad->changed_batch = 0; -- cgit v0.10.2 From 18ce3751ccd488c78d3827e9f6bf54e6322676fb Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 1 Jul 2008 09:07:34 +0200 Subject: Properly notify block layer of sync writes fsync_buffers_list() and sync_dirty_buffer() both issue async writes and then immediately wait on them. Conceptually, that makes them sync writes and we should treat them as such so that the IO schedulers can handle them appropriately. This patch fixes a write starvation issue that Lin Ming reported, where xx is stuck for more than 2 minutes because of a large number of synchronous IO in the system: INFO: task kjournald:20558 blocked for more than 120 seconds. "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. kjournald D ffff810010820978 6712 20558 2 ffff81022ddb1d10 0000000000000046 ffff81022e7baa10 ffffffff803ba6f2 ffff81022ecd0000 ffff8101e6dc9160 ffff81022ecd0348 000000008048b6cb 0000000000000086 ffff81022c4e8d30 0000000000000000 ffffffff80247537 Call Trace: [] kobject_get+0x12/0x17 [] getnstimeofday+0x2f/0x83 [] sync_buffer+0x0/0x3f [] io_schedule+0x5d/0x9f [] sync_buffer+0x3b/0x3f [] __wait_on_bit+0x40/0x6f [] sync_buffer+0x0/0x3f [] out_of_line_wait_on_bit+0x6c/0x78 [] wake_bit_function+0x0/0x23 [] sync_dirty_buffer+0x98/0xcb [] journal_commit_transaction+0x97d/0xcb6 [] lock_timer_base+0x26/0x4b [] kjournald+0xc1/0x1fb [] autoremove_wake_function+0x0/0x2e [] kjournald+0x0/0x1fb [] kthread+0x47/0x74 [] schedule_tail+0x28/0x5d [] child_rip+0xa/0x12 [] kthread+0x0/0x74 [] child_rip+0x0/0x12 Lin Ming confirms that this patch fixes the issue. I've run tests with it for the past week and no ill effects have been observed, so I'm proposing it for inclusion into 2.6.26. Signed-off-by: Jens Axboe diff --git a/fs/buffer.c b/fs/buffer.c index a073f3f..0f51c0f 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -821,7 +821,7 @@ static int fsync_buffers_list(spinlock_t *lock, struct list_head *list) * contents - it is a noop if I/O is still in * flight on potentially older contents. */ - ll_rw_block(SWRITE, 1, &bh); + ll_rw_block(SWRITE_SYNC, 1, &bh); brelse(bh); spin_lock(lock); } @@ -2940,16 +2940,19 @@ void ll_rw_block(int rw, int nr, struct buffer_head *bhs[]) for (i = 0; i < nr; i++) { struct buffer_head *bh = bhs[i]; - if (rw == SWRITE) + if (rw == SWRITE || rw == SWRITE_SYNC) lock_buffer(bh); else if (test_set_buffer_locked(bh)) continue; - if (rw == WRITE || rw == SWRITE) { + if (rw == WRITE || rw == SWRITE || rw == SWRITE_SYNC) { if (test_clear_buffer_dirty(bh)) { bh->b_end_io = end_buffer_write_sync; get_bh(bh); - submit_bh(WRITE, bh); + if (rw == SWRITE_SYNC) + submit_bh(WRITE_SYNC, bh); + else + submit_bh(WRITE, bh); continue; } } else { @@ -2978,7 +2981,7 @@ int sync_dirty_buffer(struct buffer_head *bh) if (test_clear_buffer_dirty(bh)) { get_bh(bh); bh->b_end_io = end_buffer_write_sync; - ret = submit_bh(WRITE, bh); + ret = submit_bh(WRITE_SYNC, bh); wait_on_buffer(bh); if (buffer_eopnotsupp(bh)) { clear_buffer_eopnotsupp(bh); diff --git a/include/linux/fs.h b/include/linux/fs.h index 7c10808..d8e2762 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -83,6 +83,7 @@ extern int dir_notify_enable; #define READ_SYNC (READ | (1 << BIO_RW_SYNC)) #define READ_META (READ | (1 << BIO_RW_META)) #define WRITE_SYNC (WRITE | (1 << BIO_RW_SYNC)) +#define SWRITE_SYNC (SWRITE | (1 << BIO_RW_SYNC)) #define WRITE_BARRIER ((1 << BIO_RW) | (1 << BIO_RW_BARRIER)) #define SEL_IN 1 -- cgit v0.10.2 From 8558f8f81680a43d383abd1b5f23d3501fedfa65 Mon Sep 17 00:00:00 2001 From: Gautham R Shenoy Date: Fri, 27 Jun 2008 10:17:38 +0530 Subject: rcu: fix hotplug vs rcu race Dhaval Giani reported this warning during cpu hotplug stress-tests: | On running kernel compiles in parallel with cpu hotplug: | | WARNING: at arch/x86/kernel/smp.c:118 | native_smp_send_reschedule+0x21/0x36() | Modules linked in: | Pid: 27483, comm: cc1 Not tainted 2.6.26-rc7 #1 | [...] | [] native_smp_send_reschedule+0x21/0x36 | [] force_quiescent_state+0x47/0x57 | [] call_rcu+0x51/0x6d | [] __fput+0x130/0x158 | [] fput+0x17/0x19 | [] filp_close+0x4d/0x57 | [] sys_close+0x5c/0x97 IMHO the warning is a spurious one. cpu_online_map is updated by the _cpu_down() using stop_machine_run(). Since force_quiescent_state is invoked from irqs disabled section, stop_machine_run() won't be executing while a cpu is executing force_quiescent_state(). Hence the cpu_online_map is stable while we're in the irq disabled section. However, a cpu might have been offlined _just_ before we disabled irqs while entering force_quiescent_state(). And rcu subsystem might not yet have handled the CPU_DEAD notification, leading to the offlined cpu's bit being set in the rcp->cpumask. Hence cpumask = (rcp->cpumask & cpu_online_map) to prevent sending smp_reschedule() to an offlined CPU. Here's the timeline: CPU_A CPU_B -------------------------------------------------------------- cpu_down(): . . . . . stop_machine(): /* disables preemption, . * and irqs */ . . . . . take_cpu_down(); . . . . . . . cpu_disable(); /*this removes cpu . *from cpu_online_map . */ . . . . . restart_machine(); /* enables irqs */ . ------WINDOW DURING WHICH rcp->cpumask is stale --------------- . call_rcu(); . /* disables irqs here */ . .force_quiescent_state(); .CPU_DEAD: .for_each_cpu(rcp->cpumask) . . smp_send_reschedule(); . . . . WARN_ON() for offlined CPU! . . . rcu_cpu_notify: . -------- WINDOW ENDS ------------------------------------------ rcu_offline_cpu() /* Which calls cpu_quiet() * which removes * cpu from rcp->cpumask. */ If a new batch was started just before calling stop_machine_run(), the "tobe-offlined" cpu is still present in rcp-cpumask. During a cpu-offline, from take_cpu_down(), we queue an rt-prio idle task as the next task to be picked by the scheduler. We also call cpu_disable() which will disable any further interrupts and remove the cpu's bit from the cpu_online_map. Once the stop_machine_run() successfully calls take_cpu_down(), it calls schedule(). That's the last time a schedule is called on the offlined cpu, and hence the last time when rdp->passed_quiesc will be set to 1 through rcu_qsctr_inc(). But the cpu_quiet() will be on this cpu will be called only when the next RCU_SOFTIRQ occurs on this CPU. So at this time, the offlined CPU is still set in rcp->cpumask. Now coming back to the idle_task which truely offlines the CPU, it does check for a pending RCU and raises the softirq, since it will find rdp->passed_quiesc to be 0 in this case. However, since the cpu is offline I am not sure if the softirq will trigger on the CPU. Even if it doesn't the rcu_offline_cpu() will find that rcp->completed is not the same as rcp->cur, which means that our cpu could be holding up the grace period progression. Hence we call cpu_quiet() and move ahead. But because of the window explained in the timeline, we could still have a call_rcu() before the RCU subsystem executes it's CPU_DEAD notification, and we send smp_send_reschedule() to offlined cpu while trying to force the quiescent states. The appended patch adds comments and prevents checking for offlined cpu everytime. cpu_online_map is updated by the _cpu_down() using stop_machine_run(). Since force_quiescent_state is invoked from irqs disabled section, stop_machine_run() won't be executing while a cpu is executing force_quiescent_state(). Hence the cpu_online_map is stable while we're in the irq disabled section. Reported-by: Dhaval Giani Signed-off-by: Gautham R Shenoy Acked-by: Dhaval Giani Cc: Dipankar Sarma Cc: laijs@cn.fujitsu.com Cc: Peter Zijlstra Cc: Rusty Russel Cc: "Paul E. McKenney" Signed-off-by: Ingo Molnar diff --git a/kernel/rcuclassic.c b/kernel/rcuclassic.c index f4ffbd0..a38895a 100644 --- a/kernel/rcuclassic.c +++ b/kernel/rcuclassic.c @@ -89,8 +89,22 @@ static void force_quiescent_state(struct rcu_data *rdp, /* * Don't send IPI to itself. With irqs disabled, * rdp->cpu is the current cpu. + * + * cpu_online_map is updated by the _cpu_down() + * using stop_machine_run(). Since we're in irqs disabled + * section, stop_machine_run() is not exectuting, hence + * the cpu_online_map is stable. + * + * However, a cpu might have been offlined _just_ before + * we disabled irqs while entering here. + * And rcu subsystem might not yet have handled the CPU_DEAD + * notification, leading to the offlined cpu's bit + * being set in the rcp->cpumask. + * + * Hence cpumask = (rcp->cpumask & cpu_online_map) to prevent + * sending smp_reschedule() to an offlined CPU. */ - cpumask = rcp->cpumask; + cpus_and(cpumask, rcp->cpumask, cpu_online_map); cpu_clear(rdp->cpu, cpumask); for_each_cpu_mask(cpu, cpumask) smp_send_reschedule(cpu); -- cgit v0.10.2 From 2709781be6141798162f1089df728fb218a590df Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 1 Jul 2008 11:59:41 +0100 Subject: I2C: S3C2410: Check ACK on byte transmission We should check for the reception of an ACK after transmitting each data byte. The address send has been correctly checking this, but the data write byte state should have also been checking for these failures. As part of the same fix, we remove the ACK checking from the receive path where it should not have been checking for an ACK which our hardware was sending. Signed-off-by: Ben Dooks diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 1305ef1..7ad22c5 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -323,7 +323,17 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) * end of the message, and if so, work out what to do */ + if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) { + if (iicstat & S3C2410_IICSTAT_LASTBIT) { + dev_dbg(i2c->dev, "WRITE: No Ack\n"); + + s3c24xx_i2c_stop(i2c, -ECONNREFUSED); + goto out_ack; + } + } + retry_write: + if (!is_msgend(i2c)) { byte = i2c->msg->buf[i2c->msg_ptr++]; writeb(byte, i2c->regs + S3C2410_IICDS); @@ -377,17 +387,6 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) * going to do any more read/write */ - if (!(i2c->msg->flags & I2C_M_IGNORE_NAK) && - !(is_msglast(i2c) && is_lastmsg(i2c))) { - - if (iicstat & S3C2410_IICSTAT_LASTBIT) { - dev_dbg(i2c->dev, "READ: No Ack\n"); - - s3c24xx_i2c_stop(i2c, -ECONNREFUSED); - goto out_ack; - } - } - byte = readb(i2c->regs + S3C2410_IICDS); i2c->msg->buf[i2c->msg_ptr++] = byte; -- cgit v0.10.2 From 63f5c2891eae6b4dd0538ef094e5f256d6150d7b Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 1 Jul 2008 11:59:42 +0100 Subject: I2C: S3C2410: Fixup error codes returned rom a transfer. The driver should be returning -ENXIO for transfers that do not pass the initial address byte stage. Note, also small tidyups to the driver comments in the area. Signed-off-by: Ben Dooks diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 7ad22c5..d2645da 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -290,12 +290,12 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) * bus, or started a new i2c message */ - if (iicstat & S3C2410_IICSTAT_LASTBIT && + if (iicstat & S3C2410_IICSTAT_LASTBIT && !(i2c->msg->flags & I2C_M_IGNORE_NAK)) { /* ack was not received... */ dev_dbg(i2c->dev, "ack was not received\n"); - s3c24xx_i2c_stop(i2c, -EREMOTEIO); + s3c24xx_i2c_stop(i2c, -ENXIO); goto out_ack; } @@ -305,7 +305,7 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) i2c->state = STATE_WRITE; /* terminate the transfer if there is nothing to do - * (used by the i2c probe to find devices */ + * as this is used by the i2c probe to find devices. */ if (is_lastmsg(i2c) && i2c->msg->len == 0) { s3c24xx_i2c_stop(i2c, 0); -- cgit v0.10.2 From d150a4bbd0e5c6427e66086b139953428680160b Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 1 Jul 2008 11:59:43 +0100 Subject: I2C: S3C2410: Add MODULE_ALIAS() for s3c2440 device. Add a MODULE_ALIAS() statement for the i2c-s3c2410 controller to ensure that it can be autoloaded on the S3C2440 systems that we support. Signed-off-by: Ben Dooks diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index d2645da..9e8c875 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -948,3 +948,4 @@ MODULE_DESCRIPTION("S3C24XX I2C Bus driver"); MODULE_AUTHOR("Ben Dooks, "); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:s3c2410-i2c"); +MODULE_ALIAS("platform:s3c2440-i2c"); -- cgit v0.10.2 From a94c248113b86bbbc47d027a4004b70f2be298b1 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 1 Jul 2008 17:18:17 +0100 Subject: PCI: Restrict VPD read permission to root Some PCI devices will lock up if we attempt to read from VPD addresses beyond some device-dependent limit. Until we can identify these devices and adjust the file size accordingly, only let root read VPD through sysfs to prevent a DoS by normal users. Signed-off-by: Ben Hutchings Signed-off-by: Jesse Barnes diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 6f3c744..1f855f0 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -738,7 +738,7 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) pdev->vpd->attr = attr; attr->size = pdev->vpd->ops->get_size(pdev); attr->attr.name = "vpd"; - attr->attr.mode = S_IRUGO | S_IWUSR; + attr->attr.mode = S_IRUSR | S_IWUSR; attr->read = pci_read_vpd; attr->write = pci_write_vpd; retval = sysfs_create_bin_file(&pdev->dev.kobj, attr); -- cgit v0.10.2 From 5d1a04110bfc40a86e1387b5f4382addd9aa7fbb Mon Sep 17 00:00:00 2001 From: John Linn Date: Tue, 1 Jul 2008 14:17:18 -0600 Subject: powerpc/bootwrapper: update for initrd with simpleImage This change to the makefile corrects the build of a simpleImage with initrd. Signed-off-by: John Linn Signed-off-by: Grant Likely diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 1cee2f9..095e04d 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -273,7 +273,8 @@ endif initrd- := $(patsubst zImage%, zImage.initrd%, $(image-n) $(image-)) initrd-y := $(patsubst zImage%, zImage.initrd%, \ $(patsubst dtbImage%, dtbImage.initrd%, \ - $(patsubst treeImage%, treeImage.initrd%, $(image-y)))) + $(patsubst simpleImage%, simpleImage.initrd%, \ + $(patsubst treeImage%, treeImage.initrd%, $(image-y))))) initrd-y := $(filter-out $(image-y), $(initrd-y)) targets += $(image-y) $(initrd-y) -- cgit v0.10.2 From 2260e63a2f313f416b31af80d02f02ef92d20d78 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 1 Jul 2008 22:38:18 +0200 Subject: i2c: Documentation: fix device matching description The matching process described for new style clients in Documentation/i2c/writing-clients is classed as out-of-date as it requires the presence of an .id_table entry in the driver's i2c_driver entry. Signed-off-by: Ben Dooks Signed-off-by: Jean Delvare diff --git a/Documentation/i2c/writing-clients b/Documentation/i2c/writing-clients index ee75cba..d4cd412 100644 --- a/Documentation/i2c/writing-clients +++ b/Documentation/i2c/writing-clients @@ -25,12 +25,23 @@ routines, and should be zero-initialized except for fields with data you provide. A client structure holds device-specific information like the driver model device node, and its I2C address. +/* iff driver uses driver model ("new style") binding model: */ + +static struct i2c_device_id foo_idtable[] = { + { "foo", my_id_for_foo }, + { "bar", my_id_for_bar }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, foo_idtable); + static struct i2c_driver foo_driver = { .driver = { .name = "foo", }, /* iff driver uses driver model ("new style") binding model: */ + .id_table = foo_ids, .probe = foo_probe, .remove = foo_remove, @@ -173,10 +184,9 @@ handle may be used during foo_probe(). If foo_probe() reports success (zero not a negative status code) it may save the handle and use it until foo_remove() returns. That binding model is used by most Linux drivers. -Drivers match devices when i2c_client.driver_name and the driver name are -the same; this approach is used in several other busses that don't have -device typing support in the hardware. The driver and module name should -match, so hotplug/coldplug mechanisms will modprobe the driver. +The probe function is called when an entry in the id_table name field +matches the device's name. It is passed the entry that was matched so +the driver knows which one in the table matched. Device Creation (Standard driver model) -- cgit v0.10.2 From 8e29da9ee8958cc17e27f4053420f1c982614793 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 1 Jul 2008 22:38:18 +0200 Subject: i2c: Fix bad hint about irqs in i2c.h i2c.h mentions -1 as a not-issued irq. This false hint was taken by of_i2c and caused crashes. Don't give any advice as 'no irq' is not consistent across all architectures yet and it is not needed internally by the i2c-core. Signed-off-by: Wolfram Sang Signed-off-by: Jean Delvare diff --git a/include/linux/i2c.h b/include/linux/i2c.h index fb9af6a..8dc7301 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -171,7 +171,7 @@ struct i2c_client { struct i2c_adapter *adapter; /* the adapter we sit on */ struct i2c_driver *driver; /* and our access routines */ struct device dev; /* the device structure */ - int irq; /* irq issued by device (or -1) */ + int irq; /* irq issued by device */ struct list_head list; /* DEPRECATED */ struct completion released; }; -- cgit v0.10.2 From 1e6d1f260611387a7b4e6eae4d1dd6f62e53714d Mon Sep 17 00:00:00 2001 From: John Linn Date: Tue, 1 Jul 2008 10:52:41 -0700 Subject: powerpc/legacy_serial: Bail if reg-offset/shift properties are present The legacy serial driver does not work with an 8250 type UART that is described in the device tree with the reg-offset and reg-shift properties. This change makes legacy_serial ignore these devices. Signed-off-by: John Linn Signed-off-by: Grant Likely diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index 61dd174..cf37f5c 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -136,6 +136,11 @@ static int __init add_legacy_soc_port(struct device_node *np, if (of_get_property(np, "clock-frequency", NULL) == NULL) return -1; + /* if reg-shift or offset, don't try to use it */ + if ((of_get_property(np, "reg-shift", NULL) != NULL) || + (of_get_property(np, "reg-offset", NULL) != NULL)) + return -1; + /* if rtas uses this device, don't try to use it as well */ if (of_get_property(np, "used-by-rtas", NULL) != NULL) return -1; -- cgit v0.10.2 From 18d76ac9a47742558bca3bbc2f7c41870ac744c8 Mon Sep 17 00:00:00 2001 From: Tim Yamin Date: Tue, 17 Jun 2008 09:33:14 +0100 Subject: powerpc/mpc5200: Fix lite5200b suspend/resume Suspend/resume ("echo mem > /sys/power/state") does not work with vanilla kernels -- the system does not suspend correctly and just hangs. This patch fixes this so suspend/resume works: 1) of_iomap does not map the whole 0xC000 of the MPC5200 immr so saving registers does not work. 2) PCI registers need to be saved and restored. Signed-off-by: Tim Yamin Signed-off-by: Grant Likely diff --git a/arch/powerpc/platforms/52xx/lite5200_pm.c b/arch/powerpc/platforms/52xx/lite5200_pm.c index 41c7fd9..fe92e65 100644 --- a/arch/powerpc/platforms/52xx/lite5200_pm.c +++ b/arch/powerpc/platforms/52xx/lite5200_pm.c @@ -14,6 +14,7 @@ static struct mpc52xx_sdma __iomem *bes; static struct mpc52xx_xlb __iomem *xlb; static struct mpc52xx_gpio __iomem *gps; static struct mpc52xx_gpio_wkup __iomem *gpw; +static void __iomem *pci; static void __iomem *sram; static const int sram_size = 0x4000; /* 16 kBytes */ static void __iomem *mbar; @@ -50,6 +51,8 @@ static int lite5200_pm_prepare(void) { .type = "builtin", .compatible = "mpc5200", }, /* efika */ {} }; + u64 regaddr64 = 0; + const u32 *regaddr_p; /* deep sleep? let mpc52xx code handle that */ if (lite5200_pm_target_state == PM_SUSPEND_STANDBY) @@ -60,8 +63,12 @@ static int lite5200_pm_prepare(void) /* map registers */ np = of_find_matching_node(NULL, immr_ids); - mbar = of_iomap(np, 0); + regaddr_p = of_get_address(np, 0, NULL, NULL); + if (regaddr_p) + regaddr64 = of_translate_address(np, regaddr_p); of_node_put(np); + + mbar = ioremap((u32) regaddr64, 0xC000); if (!mbar) { printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__); return -ENOSYS; @@ -71,6 +78,7 @@ static int lite5200_pm_prepare(void) pic = mbar + 0x500; gps = mbar + 0xb00; gpw = mbar + 0xc00; + pci = mbar + 0xd00; bes = mbar + 0x1200; xlb = mbar + 0x1f00; sram = mbar + 0x8000; @@ -85,6 +93,7 @@ static struct mpc52xx_sdma sbes; static struct mpc52xx_xlb sxlb; static struct mpc52xx_gpio sgps; static struct mpc52xx_gpio_wkup sgpw; +static char spci[0x200]; static void lite5200_save_regs(void) { @@ -94,6 +103,7 @@ static void lite5200_save_regs(void) _memcpy_fromio(&sxlb, xlb, sizeof(*xlb)); _memcpy_fromio(&sgps, gps, sizeof(*gps)); _memcpy_fromio(&sgpw, gpw, sizeof(*gpw)); + _memcpy_fromio(spci, pci, 0x200); _memcpy_fromio(saved_sram, sram, sram_size); } @@ -103,6 +113,8 @@ static void lite5200_restore_regs(void) int i; _memcpy_toio(sram, saved_sram, sram_size); + /* PCI Configuration */ + _memcpy_toio(pci, spci, 0x200); /* * GPIOs. Interrupt Master Enable has higher address then other -- cgit v0.10.2 From 77a538d5aa25a8866606a1faa4300c9aa2a59dfc Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 1 Jul 2008 17:22:48 -0700 Subject: ipv4: fix sysctl documentation of time related values These sysctl values are time related and all use the same routine (proc_dointvec_jiffies) that internally converts from seconds to jiffies. The code is fine, the documentation is just wrong. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 17a6e46..17f1f91 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -81,23 +81,23 @@ inet_peer_minttl - INTEGER Minimum time-to-live of entries. Should be enough to cover fragment time-to-live on the reassembling side. This minimum time-to-live is guaranteed if the pool size is less than inet_peer_threshold. - Measured in jiffies(1). + Measured in seconds. inet_peer_maxttl - INTEGER Maximum time-to-live of entries. Unused entries will expire after this period of time if there is no memory pressure on the pool (i.e. when the number of entries in the pool is very small). - Measured in jiffies(1). + Measured in seconds. inet_peer_gc_mintime - INTEGER Minimum interval between garbage collection passes. This interval is in effect under high memory pressure on the pool. - Measured in jiffies(1). + Measured in seconds. inet_peer_gc_maxtime - INTEGER Minimum interval between garbage collection passes. This interval is in effect under low (or absent) memory pressure on the pool. - Measured in jiffies(1). + Measured in seconds. TCP variables: @@ -794,10 +794,6 @@ tag - INTEGER Allows you to write a number, which can be used as required. Default value is 0. -(1) Jiffie: internal timeunit for the kernel. On the i386 1/100s, on the -Alpha 1/1024s. See the HZ define in /usr/include/asm/param.h for the exact -value on your system. - Alexey Kuznetsov. kuznet@ms2.inr.ac.ru -- cgit v0.10.2 From ff31ab56c0e900235f653e375fc3b01ba2d8d6a3 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 1 Jul 2008 19:52:38 -0700 Subject: net-sched: change tcf_destroy_chain() to clear start of filter list Pass double tcf_proto pointers to tcf_destroy_chain() to make it clear the start of the filter list for more consistency. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index ab502ec..a87fc03 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -178,7 +178,7 @@ extern struct Qdisc *qdisc_alloc(struct net_device *dev, struct Qdisc_ops *ops); extern struct Qdisc *qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops, u32 parentid); extern void tcf_destroy(struct tcf_proto *tp); -extern void tcf_destroy_chain(struct tcf_proto *fl); +extern void tcf_destroy_chain(struct tcf_proto **fl); static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff_head *list) diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 635b996..5d09e86 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -323,8 +323,7 @@ static void wme_qdiscop_destroy(struct Qdisc* qd) struct ieee80211_hw *hw = &local->hw; int queue; - tcf_destroy_chain(q->filter_list); - q->filter_list = NULL; + tcf_destroy_chain(&q->filter_list); for (queue=0; queue < hw->queues; queue++) { skb_queue_purge(&q->requeued[queue]); diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index c40773c..10f01ad 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1252,12 +1252,12 @@ void tcf_destroy(struct tcf_proto *tp) kfree(tp); } -void tcf_destroy_chain(struct tcf_proto *fl) +void tcf_destroy_chain(struct tcf_proto **fl) { struct tcf_proto *tp; - while ((tp = fl) != NULL) { - fl = tp->next; + while ((tp = *fl) != NULL) { + *fl = tp->next; tcf_destroy(tp); } } diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index 3352734..8e5f70b 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -160,7 +160,7 @@ static void atm_tc_put(struct Qdisc *sch, unsigned long cl) *prev = flow->next; pr_debug("atm_tc_put: qdisc %p\n", flow->q); qdisc_destroy(flow->q); - tcf_destroy_chain(flow->filter_list); + tcf_destroy_chain(&flow->filter_list); if (flow->sock) { pr_debug("atm_tc_put: f_count %d\n", file_count(flow->sock->file)); @@ -588,8 +588,7 @@ static void atm_tc_destroy(struct Qdisc *sch) pr_debug("atm_tc_destroy(sch %p,[qdisc %p])\n", sch, p); /* races ? */ while ((flow = p->flows)) { - tcf_destroy_chain(flow->filter_list); - flow->filter_list = NULL; + tcf_destroy_chain(&flow->filter_list); if (flow->ref > 1) printk(KERN_ERR "atm_destroy: %p->ref = %d\n", flow, flow->ref); diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 09969c1..2a3c97f 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1704,7 +1704,7 @@ static void cbq_destroy_class(struct Qdisc *sch, struct cbq_class *cl) BUG_TRAP(!cl->filters); - tcf_destroy_chain(cl->filter_list); + tcf_destroy_chain(&cl->filter_list); qdisc_destroy(cl->q); qdisc_put_rtab(cl->R_tab); gen_kill_estimator(&cl->bstats, &cl->rate_est); @@ -1728,10 +1728,8 @@ cbq_destroy(struct Qdisc* sch) * be bound to classes which have been destroyed already. --TGR '04 */ for (h = 0; h < 16; h++) { - for (cl = q->classes[h]; cl; cl = cl->next) { - tcf_destroy_chain(cl->filter_list); - cl->filter_list = NULL; - } + for (cl = q->classes[h]; cl; cl = cl->next) + tcf_destroy_chain(&cl->filter_list); } for (h = 0; h < 16; h++) { struct cbq_class *next; diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index 64465ba..c4c1317 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -416,7 +416,7 @@ static void dsmark_destroy(struct Qdisc *sch) pr_debug("dsmark_destroy(sch %p,[qdisc %p])\n", sch, p); - tcf_destroy_chain(p->filter_list); + tcf_destroy_chain(&p->filter_list); qdisc_destroy(p->q); kfree(p->mask); } diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index fdfaa3f..eca83a3 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1123,7 +1123,7 @@ hfsc_destroy_class(struct Qdisc *sch, struct hfsc_class *cl) { struct hfsc_sched *q = qdisc_priv(sch); - tcf_destroy_chain(cl->filter_list); + tcf_destroy_chain(&cl->filter_list); qdisc_destroy(cl->qdisc); gen_kill_estimator(&cl->bstats, &cl->rate_est); if (cl != &q->root) diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 6807c97..3fb58f4 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1238,7 +1238,7 @@ static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl) qdisc_put_rtab(cl->rate); qdisc_put_rtab(cl->ceil); - tcf_destroy_chain(cl->filter_list); + tcf_destroy_chain(&cl->filter_list); while (!list_empty(&cl->children)) htb_destroy_class(sch, list_entry(cl->children.next, @@ -1267,7 +1267,7 @@ static void htb_destroy(struct Qdisc *sch) and surprisingly it worked in 2.4. But it must precede it because filter need its target class alive to be able to call unbind_filter on it (without Oops). */ - tcf_destroy_chain(q->filter_list); + tcf_destroy_chain(&q->filter_list); while (!list_empty(&q->root)) htb_destroy_class(sch, list_entry(q->root.next, diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index 274b1dd..956c80a 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -104,7 +104,7 @@ static void ingress_destroy(struct Qdisc *sch) { struct ingress_qdisc_data *p = qdisc_priv(sch); - tcf_destroy_chain(p->filter_list); + tcf_destroy_chain(&p->filter_list); } static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb) diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 4aa2b45..5532f10 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -219,7 +219,7 @@ prio_destroy(struct Qdisc* sch) int prio; struct prio_sched_data *q = qdisc_priv(sch); - tcf_destroy_chain(q->filter_list); + tcf_destroy_chain(&q->filter_list); for (prio=0; priobands; prio++) qdisc_destroy(q->queues[prio]); } diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index f0463d7..6a97afb 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -520,7 +520,7 @@ static void sfq_destroy(struct Qdisc *sch) { struct sfq_sched_data *q = qdisc_priv(sch); - tcf_destroy_chain(q->filter_list); + tcf_destroy_chain(&q->filter_list); q->perturb_period = 0; del_timer_sync(&q->perturb_timer); } -- cgit v0.10.2 From a4aebb83cf0da0363684f1c339f7e6149a3e74c1 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 1 Jul 2008 19:53:09 -0700 Subject: net-sched: fix filter destruction in atm/hfsc qdisc destruction Filters need to be destroyed before beginning to destroy classes since the destination class needs to still be alive to unbind the filter. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index 8e5f70b..db0e23a 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -586,9 +586,11 @@ static void atm_tc_destroy(struct Qdisc *sch) struct atm_flow_data *flow; pr_debug("atm_tc_destroy(sch %p,[qdisc %p])\n", sch, p); + for (flow = p->flows; flow; flow = flow->next) + tcf_destroy_chain(&flow->filter_list); + /* races ? */ while ((flow = p->flows)) { - tcf_destroy_chain(&flow->filter_list); if (flow->ref > 1) printk(KERN_ERR "atm_destroy: %p->ref = %d\n", flow, flow->ref); diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index eca83a3..e817aa0 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1541,6 +1541,10 @@ hfsc_destroy_qdisc(struct Qdisc *sch) unsigned int i; for (i = 0; i < HFSC_HSIZE; i++) { + list_for_each_entry(cl, &q->clhash[i], hlist) + tcf_destroy_chain(&cl->filter_list); + } + for (i = 0; i < HFSC_HSIZE; i++) { list_for_each_entry_safe(cl, next, &q->clhash[i], hlist) hfsc_destroy_class(sch, cl); } -- cgit v0.10.2 From 8487460720fd03a0f4ecd032f017b0a8468028da Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Tue, 1 Jul 2008 19:55:09 -0700 Subject: netlink: Unneeded local variable We already have a variable, which has the same capability. Signed-off-by: Wang Chen Signed-off-by: David S. Miller diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 9b97f80..349aba1 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -886,7 +886,7 @@ retry: return netlink_unicast_kernel(sk, skb); if (sk_filter(sk, skb)) { - int err = skb->len; + err = skb->len; kfree_skb(skb); sock_put(sk); return err; -- cgit v0.10.2 From 8fde8a076940969d32805b853efdce8b988d7dda Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Tue, 1 Jul 2008 19:55:40 -0700 Subject: net: Tyop of sk_filter() comment Parameter "needlock" no long exists. Signed-off-by: Wang Chen Signed-off-by: David S. Miller diff --git a/net/core/filter.c b/net/core/filter.c index 4f83697..df37443 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -68,7 +68,6 @@ static inline void *load_pointer(struct sk_buff *skb, int k, * sk_filter - run a packet through a socket filter * @sk: sock associated with &sk_buff * @skb: buffer to filter - * @needlock: set to 1 if the sock is not locked by caller. * * Run the filter code and then cut skb->data to correct size returned by * sk_run_filter. If pkt_len is 0 we toss packet. If skb->len is smaller -- cgit v0.10.2 From 93b3cff9915322d6fa36bac0064714a7076230e4 Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Tue, 1 Jul 2008 19:57:19 -0700 Subject: netdevice: Fix wrong string handle in kernel command line parsing v1->v2: Use strlcpy() to ensure s[i].name be null-termination. 1. In netdev_boot_setup_add(), a long name will leak. ex. : dev=21,0x1234,0x1234,0x2345,eth123456789verylongname......... 2. In netdev_boot_setup_check(), mismatch will happen if s[i].name is a substring of dev->name. ex. : dev=...eth1 dev=...eth11 [ With feedback from Ben Hutchings. ] Signed-off-by: Wang Chen Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index 56b4657..fca23a3 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -454,7 +454,7 @@ static int netdev_boot_setup_add(char *name, struct ifmap *map) for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) { if (s[i].name[0] == '\0' || s[i].name[0] == ' ') { memset(s[i].name, 0, sizeof(s[i].name)); - strcpy(s[i].name, name); + strlcpy(s[i].name, name, IFNAMSIZ); memcpy(&s[i].map, map, sizeof(s[i].map)); break; } @@ -479,7 +479,7 @@ int netdev_boot_setup_check(struct net_device *dev) for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) { if (s[i].name[0] != '\0' && s[i].name[0] != ' ' && - !strncmp(dev->name, s[i].name, strlen(s[i].name))) { + !strcmp(dev->name, s[i].name)) { dev->irq = s[i].map.irq; dev->base_addr = s[i].map.base_addr; dev->mem_start = s[i].map.mem_start; -- cgit v0.10.2 From 2fe195cfe3e53c144d247b2768e37732e8eae4d8 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 1 Jul 2008 19:59:37 -0700 Subject: net: fib_rules: fix error code for unsupported families The errno code returned must be negative. Fixes "RTNETLINK answers: Unknown error 18446744073709551519". Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index e3e9ab0..277a230 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -226,7 +226,7 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) ops = lookup_rules_ops(net, frh->family); if (ops == NULL) { - err = EAFNOSUPPORT; + err = -EAFNOSUPPORT; goto errout; } @@ -365,7 +365,7 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) ops = lookup_rules_ops(net, frh->family); if (ops == NULL) { - err = EAFNOSUPPORT; + err = -EAFNOSUPPORT; goto errout; } -- cgit v0.10.2 From c7f1b2044191a82e7f0a1a674751ed582289e2e0 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Wed, 2 Jul 2008 09:34:28 +0100 Subject: dm crypt: use cond_resched Add cond_resched() to prevent monopolising CPU when processing large bios. dm-crypt processes encryption of bios in sector units. If the bio request is big it can spend a long time in the encryption call. Signed-off-by: Milan Broz Tested-by: Yan Li Signed-off-by: Andrew Morton Signed-off-by: Alasdair G Kergon diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 835def1..ab6a61d 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -432,6 +432,7 @@ static int crypt_convert(struct crypt_config *cc, case 0: atomic_dec(&ctx->pending); ctx->sector++; + cond_resched(); continue; /* error */ -- cgit v0.10.2 From 06f3ed23b1e1038da649c4836b51fe035f5536bd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 2 Jul 2008 11:03:33 -0300 Subject: V4L/DVB (8178): uvc: Fix compilation breakage for the other drivers, if uvc is selected UVC makefile defines obj as: obj-$(CONFIG_USB_VIDEO_CLASS) := uvcvideo.o Instead of: obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o Due to that, if uvc is selected, all obj-y or obj-m that were added to compilation were forget. This breaks a proper kernel build. Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/uvc/Makefile b/drivers/media/video/uvc/Makefile index fb39bbf..968c199 100644 --- a/drivers/media/video/uvc/Makefile +++ b/drivers/media/video/uvc/Makefile @@ -1,3 +1,3 @@ uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \ uvc_status.o uvc_isight.o -obj-$(CONFIG_USB_VIDEO_CLASS) := uvcvideo.o +obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o -- cgit v0.10.2 From 99cb233d60cbe644203f19938c729ea2bb004d70 Mon Sep 17 00:00:00 2001 From: Benjamin Li Date: Wed, 2 Jul 2008 10:59:04 -0700 Subject: PCI: Limit VPD read/write lengths for Broadcom 5706, 5708, 5709 rev. For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the VPD end tag will hang the device. This problem was initially observed when a vpd entry was created in sysfs ('/sys/bus/pci/devices//vpd'). A read to this sysfs entry will dump 32k of data. Reading a full 32k will cause an access beyond the VPD end tag causing the device to hang. Once the device is hung, the bnx2 driver will not be able to reset the device. We believe that it is legal to read beyond the end tag and therefore the solution is to limit the read/write length. A majority of this patch is from Matthew Wilcox who gave code for reworking the PCI vpd size information. A PCI quirk added for the Broadcom NIC's to limit the read/write's. Signed-off-by: Benjamin Li Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes diff --git a/drivers/pci/access.c b/drivers/pci/access.c index ec8f700..39bb96b 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -178,8 +178,7 @@ static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size, int ret; int begin, end, i; - if (pos < 0 || pos > PCI_VPD_PCI22_SIZE || - size > PCI_VPD_PCI22_SIZE - pos) + if (pos < 0 || pos > vpd->base.len || size > vpd->base.len - pos) return -EINVAL; if (size == 0) return 0; @@ -223,8 +222,8 @@ static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size, u32 val; int ret; - if (pos < 0 || pos > PCI_VPD_PCI22_SIZE || pos & 3 || - size > PCI_VPD_PCI22_SIZE - pos || size < 4) + if (pos < 0 || pos > vpd->base.len || pos & 3 || + size > vpd->base.len - pos || size < 4) return -EINVAL; val = (u8) *buf++; @@ -255,11 +254,6 @@ out: return 4; } -static int pci_vpd_pci22_get_size(struct pci_dev *dev) -{ - return PCI_VPD_PCI22_SIZE; -} - static void pci_vpd_pci22_release(struct pci_dev *dev) { kfree(container_of(dev->vpd, struct pci_vpd_pci22, base)); @@ -268,7 +262,6 @@ static void pci_vpd_pci22_release(struct pci_dev *dev) static struct pci_vpd_ops pci_vpd_pci22_ops = { .read = pci_vpd_pci22_read, .write = pci_vpd_pci22_write, - .get_size = pci_vpd_pci22_get_size, .release = pci_vpd_pci22_release, }; @@ -284,6 +277,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev) if (!vpd) return -ENOMEM; + vpd->base.len = PCI_VPD_PCI22_SIZE; vpd->base.ops = &pci_vpd_pci22_ops; spin_lock_init(&vpd->lock); vpd->cap = cap; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 1f855f0..9c71858 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -736,7 +736,7 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) attr = kzalloc(sizeof(*attr), GFP_ATOMIC); if (attr) { pdev->vpd->attr = attr; - attr->size = pdev->vpd->ops->get_size(pdev); + attr->size = pdev->vpd->len; attr->attr.name = "vpd"; attr->attr.mode = S_IRUSR | S_IWUSR; attr->read = pci_read_vpd; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0a497c1b..00408c9 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -21,11 +21,11 @@ extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); struct pci_vpd_ops { int (*read)(struct pci_dev *dev, int pos, int size, char *buf); int (*write)(struct pci_dev *dev, int pos, int size, const char *buf); - int (*get_size)(struct pci_dev *dev); void (*release)(struct pci_dev *dev); }; struct pci_vpd { + unsigned int len; struct pci_vpd_ops *ops; struct bin_attribute *attr; /* descriptor for sysfs VPD entry */ }; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index dabb563..a3497dc 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1670,6 +1670,48 @@ static void __devinit quirk_via_cx700_pci_parking_caching(struct pci_dev *dev) } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching); +/* + * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the + * VPD end tag will hang the device. This problem was initially + * observed when a vpd entry was created in sysfs + * ('/sys/bus/pci/devices//vpd'). A read to this sysfs entry + * will dump 32k of data. Reading a full 32k will cause an access + * beyond the VPD end tag causing the device to hang. Once the device + * is hung, the bnx2 driver will not be able to reset the device. + * We believe that it is legal to read beyond the end tag and + * therefore the solution is to limit the read/write length. + */ +static void __devinit quirk_brcm_570x_limit_vpd(struct pci_dev *dev) +{ + /* Only disable the VPD capability for 5706, 5708, and 5709 rev. A */ + if ((dev->device == PCI_DEVICE_ID_NX2_5706) || + (dev->device == PCI_DEVICE_ID_NX2_5708) || + ((dev->device == PCI_DEVICE_ID_NX2_5709) && + (dev->revision & 0xf0) == 0x0)) { + if (dev->vpd) + dev->vpd->len = 0x80; + } +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5706, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5706S, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5708, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5708S, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5709, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5709S, + quirk_brcm_570x_limit_vpd); + #ifdef CONFIG_PCI_MSI /* Some chipsets do not support MSI. We cannot easily rely on setting * PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually -- cgit v0.10.2 From a13307cef8bf51990ef1d525b1cbdcc2cfe07e2a Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Tue, 1 Jul 2008 20:02:23 -0600 Subject: PCI: acpiphp: cleanup notify handler on all root bridges During the development of the physical PCI slot patch series, Gary Hade kept on reporting strange oopses due to interactions between pci_slot and acpiphp. http://lkml.org/lkml/2007/11/28/319 find_root_bridges() unconditionally installs handle_hotplug_event_bridge() as an ACPI_SYSTEM_NOTIFY handler for all root bridges. However, during module cleanup, remove_bridge() will only remove the notify handler iff the root bridge had a hot-pluggable slot directly underneath. That is: root bridge -> hotplug slot But, if the topology looks like either of the following: root bridge -> non-hotplug slot root bridge -> p2p bridge -> hotplug slot Then we currently do not remove the notify handler from that root bridge. This can cause a kernel oops if we modprobe acpiphp later and it gets loaded somewhere else in memory. If the root bridge then receives a hotplug event, it will then attempt to call a stale, non-existent notify handler and we blow up. Much thanks goes to Gary Hade for his persistent debugging efforts. Signed-off-by: Alex Chiang Signed-off-by: Gary Hade Signed-off-by: Jesse Barnes diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 648596d..91156f8 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -700,9 +700,10 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, cleanup_p2p_bridge, NULL, NULL); - if (!(bridge = acpiphp_handle_to_bridge(handle))) - return AE_OK; - cleanup_bridge(bridge); + bridge = acpiphp_handle_to_bridge(handle); + if (bridge) + cleanup_bridge(bridge); + return AE_OK; } @@ -715,9 +716,19 @@ static void remove_bridge(acpi_handle handle) acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, cleanup_p2p_bridge, NULL, NULL); + /* + * On root bridges with hotplug slots directly underneath (ie, + * no p2p bridge inbetween), we call cleanup_bridge(). + * + * The else clause cleans up root bridges that either had no + * hotplug slots at all, or had a p2p bridge underneath. + */ bridge = acpiphp_handle_to_bridge(handle); if (bridge) cleanup_bridge(bridge); + else + acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + handle_hotplug_event_bridge); } static struct pci_dev * get_apic_pci_info(acpi_handle handle) -- cgit v0.10.2 From 83680cdbcecd1fd284ad4df060d12bf214bb63a8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 1 Jul 2008 13:55:25 +0200 Subject: drivers/input/ff-core.c needs Commit 656acd2bbc4ce7f224de499ee255698701396c48 ("Input: fix locking in force-feedback core") causes the following regression on m68k: | linux/drivers/input/ff-core.c: In function 'input_ff_upload': | linux/drivers/input/ff-core.c:172: error: dereferencing pointer to incomplete type | linux/drivers/input/ff-core.c: In function 'erase_effect': | linux/drivers/input/ff-core.c:197: error: dereferencing pointer to incomplete type | linux/drivers/input/ff-core.c:204: error: dereferencing pointer to incomplete type | make[4]: *** [drivers/input/ff-core.o] Error 1 As the incomplete type is `struct task_struct', including fixes it. Signed-off-by: Geert Uytterhoeven Acked-by: Dmitry Torokhov Signed-off-by: Linus Torvalds diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index 4c01464..72c63e5 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -28,6 +28,7 @@ #include #include #include +#include /* * Check that the effect_id is a valid effect and whether the user -- cgit v0.10.2 From 3d25802e3ba7c82457b5c12bbfeefe391d8a333e Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 1 Jul 2008 12:32:52 -0700 Subject: DRM/i915: only use tiled blits on 965+ When scheduled swaps occur, we need to blit between front & back buffers. If the buffers are tiled, we need to set the appropriate XY_SRC_COPY tile bit, but only on 965 chips, since it will cause corruption on pre-965 (e.g. 945). Bug reported by and fix tested by Tomas Janousek . Signed-off-by: Jesse Barnes Acked-by: Dave Airlie Signed-off-by: Linus Torvalds diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index f7f16e7..df03611 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c @@ -62,11 +62,11 @@ static void i915_vblank_tasklet(struct drm_device *dev) u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24); RING_LOCALS; - if (sarea_priv->front_tiled) { + if (IS_I965G(dev) && sarea_priv->front_tiled) { cmd |= XY_SRC_COPY_BLT_DST_TILED; dst_pitch >>= 2; } - if (sarea_priv->back_tiled) { + if (IS_I965G(dev) && sarea_priv->back_tiled) { cmd |= XY_SRC_COPY_BLT_SRC_TILED; src_pitch >>= 2; } -- cgit v0.10.2 From 3e2a078ca6a0d3122bbf2b904cd7ccf21a5ca21d Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 30 Jun 2008 17:40:08 +0100 Subject: tty: Fix inverted logic in send_break Not sure how this came to get inverted but it appears to have been my mess up. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index e94bee0..7501310 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -3322,7 +3322,7 @@ static int send_break(struct tty_struct *tty, unsigned int duration) msleep_interruptible(duration); tty->ops->break_ctl(tty, 0); tty_write_unlock(tty); - if (!signal_pending(current)) + if (signal_pending(current)) return -EINTR; return 0; } -- cgit v0.10.2 From 216705d2720dedf630b55d641737f430ead0c228 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 2 Jul 2008 22:48:03 +0100 Subject: x86: fix Intel Mac booting with EFI Fedora reports that mem_init()'s zap_low_mappings(), extended to SMP in 61165d7a035f6571c7576e7f51e7230157724c8d x86: fix app crashes after SMP resume causes 32-bit Intel Mac machines to reboot very early when booting with EFI. The EFI code appears to manage low mappings for itself when needed; but like many before it, confuses PSE with PAE. So it has only been mapping half the space it needed when PSE but not PAE. This remained unnoticed until we moved the SMP zap_low_mappings() before efi_enter_virtual_mode(). Presumably could have been noticed years ago if anyone ran a UP kernel on such machines? Reported-by: Peter Jones Signed-off-by: Hugh Dickins Cc: Peter Jones Cc: Glauber Costa Cc: Andrew Morton Cc: Linus Torvalds Signed-off-by: Ingo Molnar Tested-by: Peter Jones diff --git a/arch/x86/kernel/efi_32.c b/arch/x86/kernel/efi_32.c index 5d23d85..4b63c8e 100644 --- a/arch/x86/kernel/efi_32.c +++ b/arch/x86/kernel/efi_32.c @@ -49,13 +49,13 @@ void efi_call_phys_prelog(void) local_irq_save(efi_rt_eflags); /* - * If I don't have PSE, I should just duplicate two entries in page - * directory. If I have PSE, I just need to duplicate one entry in + * If I don't have PAE, I should just duplicate two entries in page + * directory. If I have PAE, I just need to duplicate one entry in * page directory. */ cr4 = read_cr4(); - if (cr4 & X86_CR4_PSE) { + if (cr4 & X86_CR4_PAE) { efi_bak_pg_dir_pointer[0].pgd = swapper_pg_dir[pgd_index(0)].pgd; swapper_pg_dir[0].pgd = @@ -93,7 +93,7 @@ void efi_call_phys_epilog(void) cr4 = read_cr4(); - if (cr4 & X86_CR4_PSE) { + if (cr4 & X86_CR4_PAE) { swapper_pg_dir[pgd_index(0)].pgd = efi_bak_pg_dir_pointer[0].pgd; } else { -- cgit v0.10.2 From 27df66a406a171308b138bd84938cb735392e15c Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 3 Jul 2008 10:14:10 +0200 Subject: arch/x86/mm/init_64.c: early_memtest(): fix types fix this warning: arch/x86/mm/init_64.c: In function 'early_memtest': arch/x86/mm/init_64.c:524: warning: passing argument 2 of 'find_e820_area_size' from incompatible pointer type Signed-off-by: Ingo Molnar diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index f6d20be..819dad9 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -506,7 +506,7 @@ early_param("memtest", parse_memtest); static void __init early_memtest(unsigned long start, unsigned long end) { - unsigned long t_start, t_size; + u64 t_start, t_size; unsigned pattern; if (!memtest_pattern) @@ -525,8 +525,9 @@ static void __init early_memtest(unsigned long start, unsigned long end) if (t_start + t_size > end) t_size = end - t_start; - printk(KERN_CONT "\n %016lx - %016lx pattern %d", - t_start, t_start + t_size, pattern); + printk(KERN_CONT "\n %016llx - %016llx pattern %d", + (unsigned long long)t_start, + (unsigned long long)t_start + t_size, pattern); memtest(t_start, t_size, pattern); -- cgit v0.10.2 From 2e4bef41a0f7df31be140ef354b9c12f2299016a Mon Sep 17 00:00:00 2001 From: Eric Van Hensbergen Date: Tue, 24 Jun 2008 17:39:39 -0500 Subject: 9p: fix O_APPEND in legacy mode The legacy protocol's open operation doesn't handle an append operation (it is expected that the client take care of it). We were incorrectly passing the extended protocol's flag through even in legacy mode. This was reported in bugzilla report #10689. This patch fixes the problem by disallowing extended protocol open modes from being passed in legacy mode and implemented append functionality on the client side by adding a seek after the open. Signed-off-by: Eric Van Hensbergen diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h index fd01d90..57997fa 100644 --- a/fs/9p/v9fs_vfs.h +++ b/fs/9p/v9fs_vfs.h @@ -51,4 +51,4 @@ int v9fs_dir_release(struct inode *inode, struct file *filp); int v9fs_file_open(struct inode *inode, struct file *file); void v9fs_inode2stat(struct inode *inode, struct p9_stat *stat); void v9fs_dentry_release(struct dentry *); -int v9fs_uflags2omode(int uflags); +int v9fs_uflags2omode(int uflags, int extended); diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index 0d55aff..52944d2 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -59,7 +59,7 @@ int v9fs_file_open(struct inode *inode, struct file *file) P9_DPRINTK(P9_DEBUG_VFS, "inode: %p file: %p \n", inode, file); v9ses = v9fs_inode2v9ses(inode); - omode = v9fs_uflags2omode(file->f_flags); + omode = v9fs_uflags2omode(file->f_flags, v9fs_extended(v9ses)); fid = file->private_data; if (!fid) { fid = v9fs_fid_clone(file->f_path.dentry); @@ -75,6 +75,8 @@ int v9fs_file_open(struct inode *inode, struct file *file) inode->i_size = 0; inode->i_blocks = 0; } + if ((file->f_flags & O_APPEND) && (!v9fs_extended(v9ses))) + generic_file_llseek(file, 0, SEEK_END); } file->private_data = fid; diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 40fa807..c95295c 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -132,10 +132,10 @@ static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode) /** * v9fs_uflags2omode- convert posix open flags to plan 9 mode bits * @uflags: flags to convert - * + * @extended: if .u extensions are active */ -int v9fs_uflags2omode(int uflags) +int v9fs_uflags2omode(int uflags, int extended) { int ret; @@ -155,14 +155,16 @@ int v9fs_uflags2omode(int uflags) break; } - if (uflags & O_EXCL) - ret |= P9_OEXCL; - if (uflags & O_TRUNC) ret |= P9_OTRUNC; - if (uflags & O_APPEND) - ret |= P9_OAPPEND; + if (extended) { + if (uflags & O_EXCL) + ret |= P9_OEXCL; + + if (uflags & O_APPEND) + ret |= P9_OAPPEND; + } return ret; } @@ -506,7 +508,7 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, flags = O_RDWR; fid = v9fs_create(v9ses, dir, dentry, NULL, perm, - v9fs_uflags2omode(flags)); + v9fs_uflags2omode(flags, v9fs_extended(v9ses))); if (IS_ERR(fid)) { err = PTR_ERR(fid); fid = NULL; -- cgit v0.10.2 From 41d54d3bf83f62d3ff5948cb788fe6007e66a0d0 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 3 Jul 2008 09:14:26 -0500 Subject: slub: Do not use 192 byte sized cache if minimum alignment is 128 byte The 192 byte cache is not necessary if we have a basic alignment of 128 byte. If it would be used then the 192 would be aligned to the next 128 byte boundary which would result in another 256 byte cache. Two 256 kmalloc caches cause sysfs to complain about a duplicate entry. MIPS needs 128 byte aligned kmalloc caches and spits out warnings on boot without this patch. Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index 71e43a1..cef6f8f 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -137,10 +137,12 @@ static __always_inline int kmalloc_index(size_t size) if (size <= KMALLOC_MIN_SIZE) return KMALLOC_SHIFT_LOW; +#if KMALLOC_MIN_SIZE <= 64 if (size > 64 && size <= 96) return 1; if (size > 128 && size <= 192) return 2; +#endif if (size <= 8) return 3; if (size <= 16) return 4; if (size <= 32) return 5; diff --git a/mm/slub.c b/mm/slub.c index 0987d1c..2c9a62d 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2995,8 +2995,6 @@ void __init kmem_cache_init(void) create_kmalloc_cache(&kmalloc_caches[1], "kmalloc-96", 96, GFP_KERNEL); caches++; - } - if (KMALLOC_MIN_SIZE <= 128) { create_kmalloc_cache(&kmalloc_caches[2], "kmalloc-192", 192, GFP_KERNEL); caches++; @@ -3026,6 +3024,16 @@ void __init kmem_cache_init(void) for (i = 8; i < KMALLOC_MIN_SIZE; i += 8) size_index[(i - 1) / 8] = KMALLOC_SHIFT_LOW; + if (KMALLOC_MIN_SIZE == 128) { + /* + * The 192 byte sized cache is not used if the alignment + * is 128 byte. Redirect kmalloc to use the 256 byte cache + * instead. + */ + for (i = 128 + 8; i <= 192; i += 8) + size_index[(i - 1) / 8] = 8; + } + slab_state = UP; /* Provide the correct kmalloc names now that the caches are up */ -- cgit v0.10.2 From 494de90098784b8e2797598cefdd34188884ec2e Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Thu, 3 Jul 2008 05:27:51 +0100 Subject: Do not overwrite nr_zones on !NUMA when initialising zlcache_ptr The non-NUMA case of build_zonelist_cache() would initialize the zlcache_ptr for both node_zonelists[] to NULL. Which is problematic, since non-NUMA only has a single node_zonelists[] entry, and trying to zero the non-existent second one just overwrote the nr_zones field instead. As kswapd uses this value to determine what reclaim work is necessary, the result is that kswapd never reclaims. This causes processes to stall frequently in low-memory situations as they always direct reclaim. This patch initialises zlcache_ptr correctly. Signed-off-by: Mel Gorman Tested-by: Dan Williams [ Simplified patch a bit ] Signed-off-by: Linus Torvalds diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 2f55295..f32fae3 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2328,7 +2328,6 @@ static void build_zonelists(pg_data_t *pgdat) static void build_zonelist_cache(pg_data_t *pgdat) { pgdat->node_zonelists[0].zlcache_ptr = NULL; - pgdat->node_zonelists[1].zlcache_ptr = NULL; } #endif /* CONFIG_NUMA */ -- cgit v0.10.2 From 1faf7f25b2aa4fcd2ae0ec2fd2e9fb9ff4bfee10 Mon Sep 17 00:00:00 2001 From: Thomas Bogendoerfer Date: Tue, 24 Jun 2008 00:48:05 +0200 Subject: [MIPS] IP32: Fix unexpected irq 71 It's possible that the crime interrupt handler is called without pending interrupts (probably a hardware issue). To avoid irritating "unexpected irq 71" messages, we now just ignore the spurious crime interrupts. Signed-off-by: Thomas Bogendoerfer Signed-off-by: Ralf Baechle diff --git a/arch/mips/sgi-ip32/ip32-irq.c b/arch/mips/sgi-ip32/ip32-irq.c index b0ea0e4..0d6b666 100644 --- a/arch/mips/sgi-ip32/ip32-irq.c +++ b/arch/mips/sgi-ip32/ip32-irq.c @@ -425,6 +425,11 @@ static void ip32_irq0(void) BUILD_BUG_ON(MACEISA_SERIAL2_RDMAOR_IRQ - MACEISA_AUDIO_SW_IRQ != 31); crime_int = crime->istat & crime_mask; + + /* crime sometime delivers spurious interrupts, ignore them */ + if (unlikely(crime_int == 0)) + return; + irq = MACE_VID_IN1_IRQ + __ffs(crime_int); if (crime_int & CRIME_MACEISA_INT_MASK) { -- cgit v0.10.2 From 7e3297dc280f88ec0c6619a895f3d449776f952e Mon Sep 17 00:00:00 2001 From: Thomas Bogendoerfer Date: Fri, 27 Jun 2008 23:52:26 +0200 Subject: [MIPS] IP22: Fix crashes due to wrong L1_CACHE_BYTES The introduction of a real dma cache invalidate makes it important to have a correct cache line size, otherwise the kernel will gives out two memory segment, which might share one cache line. The R4400 Indy/Indigo2 CPU modules are using a second level cache line size of 128 bytes, so MIPS_L1_CACHE_SHIFT needs to be bumped up to 7 for IP22. Signed-off-by: Thomas Bogendoerfer Signed-off-by: Ralf Baechle diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index e5a7c5d..24c5dee 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1006,7 +1006,7 @@ config BOOT_ELF32 config MIPS_L1_CACHE_SHIFT int default "4" if MACH_DECSTATION - default "7" if SGI_IP27 || SGI_IP28 || SNI_RM + default "7" if SGI_IP22 || SGI_IP27 || SGI_IP28 || SNI_RM default "4" if PMC_MSP4200_EVAL default "5" -- cgit v0.10.2 From 8986d2f50e1a9ba63f64ccbf59181886aa7898c3 Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Tue, 24 Jun 2008 23:26:38 +0900 Subject: [MIPS] cevt-txx9: Reset timer counter on initialization The txx9_tmr_init() will not clear a timer counter register in a certain case. The counter register is cleared on 1->0 transition of TCE bit if CRE=1. So just clearing the TCE bit is not enough. Signed-off-by: Atsushi Nemoto Signed-off-by: Ralf Baechle diff --git a/arch/mips/kernel/cevt-txx9.c b/arch/mips/kernel/cevt-txx9.c index 795cb8f..b5fc4eb 100644 --- a/arch/mips/kernel/cevt-txx9.c +++ b/arch/mips/kernel/cevt-txx9.c @@ -161,6 +161,9 @@ void __init txx9_tmr_init(unsigned long baseaddr) struct txx9_tmr_reg __iomem *tmrptr; tmrptr = ioremap(baseaddr, sizeof(struct txx9_tmr_reg)); + /* Start once to make CounterResetEnable effective */ + __raw_writel(TXx9_TMTCR_CRE | TXx9_TMTCR_TCE, &tmrptr->tcr); + /* Stop and reset the counter */ __raw_writel(TXx9_TMTCR_CRE, &tmrptr->tcr); __raw_writel(0, &tmrptr->tisr); __raw_writel(0xffffffff, &tmrptr->cpra); -- cgit v0.10.2 From ee3ece830f6db9837f7ac67008f532a8c1e755f4 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 3 Jul 2008 14:31:26 -0400 Subject: hrtimer: prevent migration for raising softirq Due to a possible deadlock, the waking of the softirq was pushed outside of the hrtimer base locks. See commit 0c96c5979a522c3323c30a078a70120e29b5bdbc Unfortunately this allows the task to migrate after setting up the softirq and raising it. Since softirqs run a queue that is per-cpu we may raise the softirq on the wrong CPU and this will keep the queued softirq task from running. To solve this issue, this patch disables preemption around the releasing of the hrtimer lock and raising of the softirq. Signed-off-by: Steven Rostedt Signed-off-by: Linus Torvalds diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 421be5f..ab80515 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -1003,10 +1003,18 @@ hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode) */ raise = timer->state == HRTIMER_STATE_PENDING; + /* + * We use preempt_disable to prevent this task from migrating after + * setting up the softirq and raising it. Otherwise, if me migrate + * we will raise the softirq on the wrong CPU. + */ + preempt_disable(); + unlock_hrtimer_base(timer, &flags); if (raise) hrtimer_raise_softirq(); + preempt_enable(); return ret; } -- cgit v0.10.2 From b620754bfeb8b0e0c6622b03d5ee2f1af1d3082f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 3 Jul 2008 15:26:35 -0400 Subject: svcrpc: fix handling of garbage args To return garbage_args, the accept_stat must be 0, and we must have a verifier. So we shouldn't be resetting the write pointer as we reject the call. Also, we must add the two placeholder words here regardless of success of the unwrap, to ensure the output buffer is left in a consistent state for svcauth_gss_release(). This fixes a BUG() in svcauth_gss.c:svcauth_gss_release(). Thanks to Aime Le Rouzic for bug report, debugging help, and testing. Signed-off-by: J. Bruce Fields Tested-by: Aime Le Rouzic Signed-off-by: Linus Torvalds diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 5905d56..81ae3d6 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -1144,20 +1144,20 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) case RPC_GSS_SVC_NONE: break; case RPC_GSS_SVC_INTEGRITY: + /* placeholders for length and seq. number: */ + svc_putnl(resv, 0); + svc_putnl(resv, 0); if (unwrap_integ_data(&rqstp->rq_arg, gc->gc_seq, rsci->mechctx)) goto garbage_args; + break; + case RPC_GSS_SVC_PRIVACY: /* placeholders for length and seq. number: */ svc_putnl(resv, 0); svc_putnl(resv, 0); - break; - case RPC_GSS_SVC_PRIVACY: if (unwrap_priv_data(rqstp, &rqstp->rq_arg, gc->gc_seq, rsci->mechctx)) goto garbage_args; - /* placeholders for length and seq. number: */ - svc_putnl(resv, 0); - svc_putnl(resv, 0); break; default: goto auth_err; @@ -1170,8 +1170,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) goto out; } garbage_args: - /* Restore write pointer to its original value: */ - xdr_ressize_check(rqstp, reject_stat); ret = SVC_GARBAGE; goto out; auth_err: -- cgit v0.10.2 From 3ee38d8bf46b364b1ca364ddb7c379a4afcd8bbb Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Sun, 8 Jun 2008 17:20:11 +0100 Subject: OHCI: Fix problem if SM501 and another platform driver is selected If the SM501 and another platform driver, such as the SM501 then we end up defining PLATFORM_DRIVER twice. This patch seperated the SM501 onto a seperate define of SM501_OHCI_DRIVER so that it can be selected without overwriting the original definition. Signed-off-by: Ben Dooks Acked-by: David Brownell Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 33f1c1c..a8160d6 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1054,7 +1054,7 @@ MODULE_LICENSE ("GPL"); #ifdef CONFIG_MFD_SM501 #include "ohci-sm501.c" -#define PLATFORM_DRIVER ohci_hcd_sm501_driver +#define SM501_OHCI_DRIVER ohci_hcd_sm501_driver #endif #if !defined(PCI_DRIVER) && \ @@ -1062,6 +1062,7 @@ MODULE_LICENSE ("GPL"); !defined(OF_PLATFORM_DRIVER) && \ !defined(SA1111_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && \ + !defined(SM501_OHCI_DRIVER) && \ !defined(SSB_OHCI_DRIVER) #error "missing bus glue for ohci-hcd" #endif @@ -1121,9 +1122,18 @@ static int __init ohci_hcd_mod_init(void) goto error_ssb; #endif +#ifdef SM501_OHCI_DRIVER + retval = platform_driver_register(&SM501_OHCI_DRIVER); + if (retval < 0) + goto error_sm501; +#endif + return retval; /* Error path */ +#ifdef SM501_OHCI_DRIVER + error_sm501: +#endif #ifdef SSB_OHCI_DRIVER error_ssb: #endif @@ -1159,6 +1169,9 @@ module_init(ohci_hcd_mod_init); static void __exit ohci_hcd_mod_exit(void) { +#ifdef SM501_OHCI_DRIVER + platform_driver_unregister(&SM501_OHCI_DRIVER); +#endif #ifdef SSB_OHCI_DRIVER ssb_driver_unregister(&SSB_OHCI_DRIVER); #endif -- cgit v0.10.2 From 4b828abed217527ca815727a1a251334bd8e5e04 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 17 Jun 2008 22:30:48 +0200 Subject: USB: fix cdc-acm resume() cdc-acm has - a memory leak in resume() - will fail to reactivate the read code path if this is needed. his corrects it by deleting the useless relict code. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 63c3404..c3201aff 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1125,9 +1125,6 @@ static void stop_data_traffic(struct acm *acm) for (i = 0; i < acm->rx_buflimit; i++) usb_kill_urb(acm->ru[i].urb); - INIT_LIST_HEAD(&acm->filled_read_bufs); - INIT_LIST_HEAD(&acm->spare_read_bufs); - tasklet_enable(&acm->urb_task); cancel_work_sync(&acm->work); -- cgit v0.10.2 From 056761e55c8687ddf3db14226213f2e8dc2689bc Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 13 Jun 2008 23:56:48 -0700 Subject: USB: ehci - fix timer regression This patch fixes a regression in the EHCI driver's TIMER_IO_WATCHDOG behavior. The patch "USB: EHCI: add separate IAA watchdog timer" changed how that timer is handled, so that short timeouts on the remaining timer (unfortunately, overloaded) would never be used. This takes a more direct approach, reorganizing the code slightly to be explicit about only the I/O watchdog role now being overridable. It also replaces a now-obsolete comment describing older timer behavior. Signed-off-by: David Brownell Cc: Alan Stern Cc: Leonid Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 35a0309..90245fd 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -177,6 +177,15 @@ timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action) static inline void timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) { + /* Don't override timeouts which shrink or (later) disable + * the async ring; just the I/O watchdog. Note that if a + * SHRINK were pending, OFF would never be requested. + */ + if (timer_pending(&ehci->watchdog) + && ((BIT(TIMER_ASYNC_SHRINK) | BIT(TIMER_ASYNC_OFF)) + & ehci->actions)) + return; + if (!test_and_set_bit (action, &ehci->actions)) { unsigned long t; @@ -192,15 +201,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) t = EHCI_SHRINK_JIFFIES; break; } - t += jiffies; - // all timings except IAA watchdog can be overridden. - // async queue SHRINK often precedes IAA. while it's ready - // to go OFF neither can matter, and afterwards the IO - // watchdog stops unless there's still periodic traffic. - if (time_before_eq(t, ehci->watchdog.expires) - && timer_pending (&ehci->watchdog)) - return; - mod_timer (&ehci->watchdog, t); + mod_timer(&ehci->watchdog, t + jiffies); } } -- cgit v0.10.2 From 29c8f6a727a683b5988877dd80dbdefd49e64a51 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 13 Jun 2008 23:59:54 -0700 Subject: USB: ohci - record data toggle after unlink This patch fixes a problem with OHCI where canceling bulk or interrupt URBs may lose track of the right data toggle. This seems to be a longstanding bug, possibly dating back to the Linux 2.4 kernel, which stayed hidden because (a) about half the time the data toggle bit was correct; (b) canceling such URBs is unusual; and (c) the few drivers which cancel these URBs either [1] do it only as part of shutting down, or [2] have fault recovery logic, which recovers. For those transfer types, the toggle is normally written back into the ED when each TD is retired. But canceling bypasses the mechanism used to retire TDs ... so on average, half the time the toggle bit will be invalid after cancelation. The fix is simple: the toggle state of any canceled TDs are propagated back to the ED in the finish_unlinks function. (Issue found by leonidv11@gmail.com ...) Signed-off-by: David Brownell Cc: Leonid Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 9c9f3b5..9b54740 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -952,6 +952,7 @@ rescan_this: struct urb *urb; urb_priv_t *urb_priv; __hc32 savebits; + u32 tdINFO; td = list_entry (entry, struct td, td_list); urb = td->urb; @@ -966,6 +967,17 @@ rescan_this: savebits = *prev & ~cpu_to_hc32 (ohci, TD_MASK); *prev = td->hwNextTD | savebits; + /* If this was unlinked, the TD may not have been + * retired ... so manually save the data toggle. + * The controller ignores the value we save for + * control and ISO endpoints. + */ + tdINFO = hc32_to_cpup(ohci, &td->hwINFO); + if ((tdINFO & TD_T) == TD_T_DATA0) + ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_C); + else if ((tdINFO & TD_T) == TD_T_DATA1) + ed->hwHeadP |= cpu_to_hc32(ohci, ED_C); + /* HC may have partly processed this TD */ td_done (ohci, urb, td); urb_priv->td_cnt++; -- cgit v0.10.2 From e72616f429a6aaa720a2d90b8fe94869f3c3ff4b Mon Sep 17 00:00:00 2001 From: matthieu castet Date: Mon, 16 Jun 2008 19:49:06 +0200 Subject: USB: mass storage: new id for US_SC_CYP_ATACB CY7C68310 chip also support cypress atacb "ATA command" pass_thru. Signed-off-by: Matthieu CASTET Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 45fe366..39a7c11 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -402,11 +402,19 @@ UNUSUAL_DEV( 0x04a5, 0x3010, 0x0100, 0x0100, US_FL_IGNORE_RESIDUE ), #ifdef CONFIG_USB_STORAGE_CYPRESS_ATACB +/* CY7C68300 : support atacb */ UNUSUAL_DEV( 0x04b4, 0x6830, 0x0000, 0x9999, "Cypress", "Cypress AT2LP", US_SC_CYP_ATACB, US_PR_DEVICE, NULL, 0), + +/* CY7C68310 : support atacb and atacb2 */ +UNUSUAL_DEV( 0x04b4, 0x6831, 0x0000, 0x9999, + "Cypress", + "Cypress ISD-300LP", + US_SC_CYP_ATACB, US_PR_DEVICE, NULL, + 0), #endif /* Reported by Simon Levitt -- cgit v0.10.2 From f15e39739a1d7dfaa2173a91707a74c11a246648 Mon Sep 17 00:00:00 2001 From: Will Newton Date: Fri, 27 Jun 2008 13:08:08 +0100 Subject: sisusbvga: Fix oops on disconnect. Remove dev_info call on disconnect. The sisusb_dev pointer may have been set to zero by sisusb_delete at this point causing an oops. The message does not provide any extra information over the standard USB subsystem output so removing it does not affect functionality. Signed-off-by: Will Newton Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index cb7fa0e..33182f4 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -3264,8 +3264,6 @@ static void sisusb_disconnect(struct usb_interface *intf) /* decrement our usage count */ kref_put(&sisusb->kref, sisusb_delete); - - dev_info(&sisusb->sisusb_dev->dev, "Disconnected\n"); } static struct usb_device_id sisusb_table [] = { -- cgit v0.10.2 From 2542335ccf34cfb442d3fd842d7e78ca5e649951 Mon Sep 17 00:00:00 2001 From: Jon K Hellan Date: Tue, 24 Jun 2008 11:43:13 +0200 Subject: USB: New device ID for ftdi_sio driver Here's a new device ID for the ftdio_sio driver. The diff is with linus's tree as of this morning. The device is the RigExpert Tiny USB Soundcard Transceiver Interface for ham radio. (I didn't actually test this. A fellow ham couldn't get the device to work, and I suggested binding the device ID using sysfs - see "http://jk.ufisa.uninett.no/usb/". However, he had had moved on to other things by then. I guess adding the device ID to the kernel "on spec" won't hurt. The relevant part of cat /proc/bus/usb/devices shows: T: Bus=02 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=0403 ProdID=ed22 Rev= 5.00 S: Manufacturer=FTDI S: Product=MixW RigExpert Tiny S: SerialNumber=00000000 C:* #Ifs= 2 Cfg#= 1 Atr=80 MxPwr=100mA I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) E: Ad=81(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) E: Ad=83(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms ) From: Jon K Hellan Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 5234e7a..0ff4a39 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -637,6 +637,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, + { USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 06e0eca..8302eca 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -828,6 +828,9 @@ /* Propox devices */ #define FTDI_PROPOX_JTAGCABLEII_PID 0xD738 +/* Rig Expert Ukraine devices */ +#define FTDI_REU_TINY_PID 0xED22 /* RigExpert Tiny */ + /* Commands */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ -- cgit v0.10.2 From de85422b94ddb23c021126815ea49414047c13dc Mon Sep 17 00:00:00 2001 From: Stefan Becker Date: Tue, 1 Jul 2008 19:19:22 +0300 Subject: USB: fix interrupt disabling for HCDs with shared interrupt handlers USB: fix interrupt disabling for HCDs with shared interrupt handlers As has been discussed several times on LKML, IRQF_SHARED | IRQF_DISABLED doesn't work reliably, i.e. a shared interrupt handler CAN'T be certain to be called with interrupts disabled. Most USB HCD handlers use IRQF_DISABLED and therefore havoc can break out if they share their interrupt with a handler that doesn't use it. On my test machine the yenta_socket interrupt handler (no IRQF_DISABLED) was registered before ehci_hcd and one uhci_hcd instance. Therefore all usb_hcd_irq() invocations for ehci_hcd and for one uhci_hcd instance happened with interrupts enabled. That led to random lockups as USB core HCD functions that acquire the same spinlock could be called twice from interrupt handlers. This patch updates usb_hcd_irq() to always disable/restore interrupts. usb_add_hcd() will silently remove any IRQF_DISABLED requested from HCD code. Signed-off-by: Stefan Becker Cc: stable Acked-by: David Brownell Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 09a53e7..7158dbb 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1684,19 +1684,30 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum); irqreturn_t usb_hcd_irq (int irq, void *__hcd) { struct usb_hcd *hcd = __hcd; - int start = hcd->state; + unsigned long flags; + irqreturn_t rc; - if (unlikely(start == HC_STATE_HALT || - !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) - return IRQ_NONE; - if (hcd->driver->irq (hcd) == IRQ_NONE) - return IRQ_NONE; + /* IRQF_DISABLED doesn't work correctly with shared IRQs + * when the first handler doesn't use it. So let's just + * assume it's never used. + */ + local_irq_save(flags); - set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + if (unlikely(hcd->state == HC_STATE_HALT || + !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { + rc = IRQ_NONE; + } else if (hcd->driver->irq(hcd) == IRQ_NONE) { + rc = IRQ_NONE; + } else { + set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); - if (unlikely(hcd->state == HC_STATE_HALT)) - usb_hc_died (hcd); - return IRQ_HANDLED; + if (unlikely(hcd->state == HC_STATE_HALT)) + usb_hc_died(hcd); + rc = IRQ_HANDLED; + } + + local_irq_restore(flags); + return rc; } /*-------------------------------------------------------------------------*/ @@ -1860,6 +1871,13 @@ int usb_add_hcd(struct usb_hcd *hcd, /* enable irqs just before we start the controller */ if (hcd->driver->irq) { + + /* IRQF_DISABLED doesn't work as advertised when used together + * with IRQF_SHARED. As usb_hcd_irq() will always disable + * interrupts we can remove it here. + */ + irqflags &= ~IRQF_DISABLED; + snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", hcd->driver->description, hcd->self.busnum); if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags, -- cgit v0.10.2 From 1236edf1c70107a0d31b3fba0b2a8783615d0d24 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 1 Jul 2008 10:45:51 -0400 Subject: USB: don't lose disconnections during suspend This patch (as1111) fixes a bug in the hub driver. When a hub resumes, disconnections that occurred while the hub was suspended are lost. A completely different fix for this problem has already been accepted for 2.6.27; however the problem still needs to be handled in 2.6.26. Signed-off-by: Alan Stern Tested-by: Lukas Hejtmanek Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 94789be..512d2d5 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -713,18 +713,11 @@ static void hub_restart(struct usb_hub *hub, int type) } /* Was the power session lost while we were suspended? */ - switch (type) { - case HUB_RESET_RESUME: - portstatus = 0; - portchange = USB_PORT_STAT_C_CONNECTION; - break; + status = hub_port_status(hub, port1, &portstatus, &portchange); - case HUB_RESET: - case HUB_RESUME: - status = hub_port_status(hub, port1, - &portstatus, &portchange); - break; - } + /* If the device is gone, khubd will handle it later */ + if (status == 0 && !(portstatus & USB_PORT_STAT_CONNECTION)) + continue; /* For "USB_PERSIST"-enabled children we must * mark the child device for reset-resume and -- cgit v0.10.2 From d2e2affba4a3619df203d3be8d655ec48d00e3ec Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 1 Jul 2008 13:11:56 +0530 Subject: USB: another option device id Thanks to umesh b for the information here. Cc: umesh b Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 43cfde8..a73420d 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -306,6 +306,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) }, { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) }, { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) }, + { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */ { USB_DEVICE(MAXON_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) }, -- cgit v0.10.2 From 727df3569b358ef440683787c2b9fe8cc55a0954 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 2 Jul 2008 15:25:41 -0500 Subject: USB: add a pl2303 device id As reported by Ken A Scott Cc: Ken A Scott Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 103195a..2a0dd1b 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -57,6 +57,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index cff160a..6ac3bbc 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -15,6 +15,7 @@ #define PL2303_PRODUCT_ID_RSAQ3 0xaaa2 #define PL2303_PRODUCT_ID_ALDIGA 0x0611 #define PL2303_PRODUCT_ID_MMX 0x0612 +#define PL2303_PRODUCT_ID_GPRS 0x0609 #define ATEN_VENDOR_ID 0x0557 #define ATEN_VENDOR_ID2 0x0547 -- cgit v0.10.2 From 4edb966b375dfbabfc96b580a164c5ae90584aa0 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 3 Jul 2008 10:05:57 +0200 Subject: USB: fix Oops on loading ipaq module since 2.6.26 Fixes bugzilla.kernel.org #10868 Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index ea924dc..5e15dec 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -570,7 +570,7 @@ static struct usb_serial_driver ipaq_device = { .description = "PocketPC PDA", .usb_driver = &ipaq_driver, .id_table = ipaq_id_table, - .num_ports = 2, + .num_ports = 1, .open = ipaq_open, .close = ipaq_close, .attach = ipaq_startup, -- cgit v0.10.2 From b89cbb81aec2015b4020221564ced1569e1a8900 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 3 Jul 2008 17:14:16 +0200 Subject: USB: adding comment for ipaq forcing number of ports The reason for forcing a number of ports should be documented. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 5e15dec..d9fb376 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -570,6 +570,11 @@ static struct usb_serial_driver ipaq_device = { .description = "PocketPC PDA", .usb_driver = &ipaq_driver, .id_table = ipaq_id_table, + /* + * some devices have an extra endpoint, which + * must be ignored as it would make the core + * create a second port which oopses when used + */ .num_ports = 1, .open = ipaq_open, .close = ipaq_close, -- cgit v0.10.2 From 50952026036c7b4212b90091bf23a264c0ccc1fb Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 3 Jul 2008 23:28:35 +0100 Subject: [MIPS] Fix bug in atomic_sub_if_positive. The branch optimization fixes in 2.6.21 introduced a bug in atomic_sub_if_positive that causes it to return even when the sc instruction fails. The result is that e.g. down_trylock becomes unreliable as the semaphore counter is not always decremented. Original MUA-shredded patch from Morten Larsen . Signed-off-by: Ralf Baechle diff --git a/include/asm-mips/atomic.h b/include/asm-mips/atomic.h index a798d62..1232be3 100644 --- a/include/asm-mips/atomic.h +++ b/include/asm-mips/atomic.h @@ -283,10 +283,10 @@ static __inline__ int atomic_sub_if_positive(int i, atomic_t * v) " beqz %0, 2f \n" " subu %0, %1, %3 \n" " .set reorder \n" - "1: \n" " .subsection 2 \n" "2: b 1b \n" " .previous \n" + "1: \n" " .set mips0 \n" : "=&r" (result), "=&r" (temp), "=m" (v->counter) : "Ir" (i), "m" (v->counter) @@ -664,10 +664,10 @@ static __inline__ long atomic64_sub_if_positive(long i, atomic64_t * v) " beqz %0, 2f \n" " dsubu %0, %1, %3 \n" " .set reorder \n" - "1: \n" " .subsection 2 \n" "2: b 1b \n" " .previous \n" + "1: \n" " .set mips0 \n" : "=&r" (result), "=&r" (temp), "=m" (v->counter) : "Ir" (i), "m" (v->counter) -- cgit v0.10.2 From d8355aca23863be659ec5b7e0393cfbfa91ec221 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Thu, 3 Jul 2008 22:10:18 -0700 Subject: xen: fix address truncation in pte mfn<->pfn conversion When converting the page number in a pte/pmd/pud/pgd between machine and pseudo-physical addresses, the converted result was being truncated at 32-bits. This caused failures on machines with more than 4G of physical memory. Signed-off-by: Jeremy Fitzhardinge Cc: "Christopher S. Aker" Cc: Ian Campbell Signed-off-by: Ingo Molnar diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index df40bf7..4e527e7 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -185,7 +185,7 @@ static pteval_t pte_mfn_to_pfn(pteval_t val) if (val & _PAGE_PRESENT) { unsigned long mfn = (val & PTE_MASK) >> PAGE_SHIFT; pteval_t flags = val & ~PTE_MASK; - val = (mfn_to_pfn(mfn) << PAGE_SHIFT) | flags; + val = ((pteval_t)mfn_to_pfn(mfn) << PAGE_SHIFT) | flags; } return val; @@ -196,7 +196,7 @@ static pteval_t pte_pfn_to_mfn(pteval_t val) if (val & _PAGE_PRESENT) { unsigned long pfn = (val & PTE_MASK) >> PAGE_SHIFT; pteval_t flags = val & ~PTE_MASK; - val = (pfn_to_mfn(pfn) << PAGE_SHIFT) | flags; + val = ((pteval_t)pfn_to_mfn(pfn) << PAGE_SHIFT) | flags; } return val; -- cgit v0.10.2 From 464b3286b4aa459059c6fda85ba55185fd21d9fc Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 2 Jul 2008 17:50:23 +0900 Subject: sata_sil24: add DID for another adaptec flavor There's another DID used for Adaptec card. Add it. Reported by Travis Read. Signed-off-by: Tejun Heo Cc: Travis Read Signed-off-by: Jeff Garzik diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index 8ee6b5b..84ffcc2 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -370,6 +370,7 @@ static const struct pci_device_id sil24_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x3124), BID_SIL3124 }, { PCI_VDEVICE(CMD, 0x3132), BID_SIL3132 }, { PCI_VDEVICE(CMD, 0x0242), BID_SIL3132 }, + { PCI_VDEVICE(CMD, 0x0244), BID_SIL3132 }, { PCI_VDEVICE(CMD, 0x3131), BID_SIL3131 }, { PCI_VDEVICE(CMD, 0x3531), BID_SIL3131 }, -- cgit v0.10.2 From ea0c62f7cf70f13a67830471b613337bd0c9a62e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 28 Jun 2008 01:49:02 +0900 Subject: ahci: always clear all bits in irq_stat Some AHCI controllers (ICH7 was reported) set pending bit in HOST_IRQ_STAT for non-existent ports and when it's not cleared falls into IRQ storm. Always clear full irq_stat instead of only the bits that are handled. As nothing changes for recognized ports, the risk of breaking things is pretty low. Reported and verified by Philipp Thomas in the following suse bugzilla. https://bugzilla.novell.com/attachment.cgi?id=215692 Signed-off-by: Tejun Heo Cc: Philipp Thomas Signed-off-by: Jeff Garzik diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 6a4a2a2..061817a 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1777,7 +1777,7 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) struct ahci_host_priv *hpriv; unsigned int i, handled = 0; void __iomem *mmio; - u32 irq_stat, irq_ack = 0; + u32 irq_stat; VPRINTK("ENTER\n"); @@ -1809,14 +1809,11 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) "interrupt on disabled port %u\n", i); } - irq_ack |= (1 << i); - } - - if (irq_ack) { - writel(irq_ack, mmio + HOST_IRQ_STAT); handled = 1; } + writel(irq_stat, mmio + HOST_IRQ_STAT); + spin_unlock(&host->lock); VPRINTK("EXIT\n"); -- cgit v0.10.2 From a836d3e882161c562b3ddacee5d8842a033c5b2c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 28 Jun 2008 01:39:43 +0900 Subject: libata-sff: improve HSM violation reporting Improve SFF HSM violation reporting such that each HSM violation can be distinguished using ehi_desc. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 215d186..c0908c2 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1094,6 +1094,7 @@ static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq) int ata_sff_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc, u8 status, int in_wq) { + struct ata_eh_info *ehi = &ap->link.eh_info; unsigned long flags = 0; int poll_next; @@ -1125,9 +1126,12 @@ fsm_start: if (likely(status & (ATA_ERR | ATA_DF))) /* device stops HSM for abort/error */ qc->err_mask |= AC_ERR_DEV; - else + else { /* HSM violation. Let EH handle this */ + ata_ehi_push_desc(ehi, + "ST_FIRST: !(DRQ|ERR|DF)"); qc->err_mask |= AC_ERR_HSM; + } ap->hsm_task_state = HSM_ST_ERR; goto fsm_start; @@ -1146,9 +1150,9 @@ fsm_start: * the CDB. */ if (!(qc->dev->horkage & ATA_HORKAGE_STUCK_ERR)) { - ata_port_printk(ap, KERN_WARNING, - "DRQ=1 with device error, " - "dev_stat 0x%X\n", status); + ata_ehi_push_desc(ehi, "ST_FIRST: " + "DRQ=1 with device error, " + "dev_stat 0x%X", status); qc->err_mask |= AC_ERR_HSM; ap->hsm_task_state = HSM_ST_ERR; goto fsm_start; @@ -1205,9 +1209,9 @@ fsm_start: * let the EH abort the command or reset the device. */ if (unlikely(status & (ATA_ERR | ATA_DF))) { - ata_port_printk(ap, KERN_WARNING, "DRQ=1 with " - "device error, dev_stat 0x%X\n", - status); + ata_ehi_push_desc(ehi, "ST-ATAPI: " + "DRQ=1 with device error, " + "dev_stat 0x%X", status); qc->err_mask |= AC_ERR_HSM; ap->hsm_task_state = HSM_ST_ERR; goto fsm_start; @@ -1226,13 +1230,17 @@ fsm_start: if (likely(status & (ATA_ERR | ATA_DF))) /* device stops HSM for abort/error */ qc->err_mask |= AC_ERR_DEV; - else + else { /* HSM violation. Let EH handle this. * Phantom devices also trigger this * condition. Mark hint. */ + ata_ehi_push_desc(ehi, "ST-ATA: " + "DRQ=1 with device error, " + "dev_stat 0x%X", status); qc->err_mask |= AC_ERR_HSM | AC_ERR_NODEV_HINT; + } ap->hsm_task_state = HSM_ST_ERR; goto fsm_start; @@ -1257,8 +1265,12 @@ fsm_start: status = ata_wait_idle(ap); } - if (status & (ATA_BUSY | ATA_DRQ)) + if (status & (ATA_BUSY | ATA_DRQ)) { + ata_ehi_push_desc(ehi, "ST-ATA: " + "BUSY|DRQ persists on ERR|DF, " + "dev_stat 0x%X", status); qc->err_mask |= AC_ERR_HSM; + } /* ata_pio_sectors() might change the * state to HSM_ST_LAST. so, the state -- cgit v0.10.2 From c7843e8f565f624b0cff7cad1370fad4cb84dfbc Mon Sep 17 00:00:00 2001 From: Mark Lord Date: Wed, 18 Jun 2008 21:57:42 -0400 Subject: sata_mv: safer logic for limit_warnings There is a miniscule chance that two separate host controllers might be in sata_mv at the same time and manage to decrement the static limit_warnings variable below zero. Fix the comparison to deal with it. Signed-off-by: Mark Lord Signed-off-by: Jeff Garzik diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 28092bc..ad169ff 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -1607,7 +1607,7 @@ static unsigned int mv_qc_issue(struct ata_queued_cmd *qc) * Much of the time, this could just work regardless. * So for now, just log the incident, and allow the attempt. */ - if (limit_warnings && (qc->nbytes / qc->sect_size) > 1) { + if (limit_warnings > 0 && (qc->nbytes / qc->sect_size) > 1) { --limit_warnings; ata_link_printk(qc->dev->link, KERN_WARNING, DRV_NAME ": attempting PIO w/multiple DRQ: " -- cgit v0.10.2 From 852bb9f594e0ea737f83487962a040ad210e8a13 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 4 Jul 2008 21:04:42 +1000 Subject: Update maintainers for powerpc This updates the MAINTAINERS entries for powerpc. It adds Ben H to the overall Linux for PowerPC entry and makes it clear this covers both 32-bit and 64-bit machines. It removes the separate entry we had for Linux on 64-bit PowerPC where Anton and I were listed as maintainers - Anton hasn't been involved in the day-to-day maintenance of the code for several years. Finally, it removes the entry for the Linux for PowerPC boot code where Tom Rini was listed as the maintainer. That code got completely rewritten when we merged 32-bit and 64-bit, and I and the various platform maintainers have been maintaining that code since. Signed-off-by: Paul Mackerras Acked-by: Tom Rini Signed-off-by: Linus Torvalds diff --git a/MAINTAINERS b/MAINTAINERS index e6c06fa..460e699 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2474,9 +2474,11 @@ M: James.Bottomley@HansenPartnership.com W: http://www.hansenpartnership.com/voyager S: Maintained -LINUX FOR POWERPC +LINUX FOR POWERPC (32-BIT AND 64-BIT) P: Paul Mackerras M: paulus@samba.org +P: Benjamin Herrenschmidt +M: benh@kernel.crashing.org W: http://www.penguinppc.org/ L: linuxppc-dev@ozlabs.org T: git kernel.org:/pub/scm/linux/kernel/git/paulus/powerpc.git @@ -2516,13 +2518,6 @@ W: http://wiki.secretlab.ca/index.php/Linux_on_Xilinx_Virtex L: linuxppc-dev@ozlabs.org S: Maintained -LINUX FOR POWERPC BOOT CODE -P: Tom Rini -M: trini@kernel.crashing.org -W: http://www.penguinppc.org/ -L: linuxppc-dev@ozlabs.org -S: Maintained - LINUX FOR POWERPC EMBEDDED PPC8XX P: Vitaly Bordug M: vitb@kernel.crashing.org @@ -2551,17 +2546,6 @@ P: Arnaldo Carvalho de Melo M: acme@ghostprotocols.net S: Maintained -LINUX FOR 64BIT POWERPC -P: Paul Mackerras -M: paulus@samba.org -M: paulus@au.ibm.com -P: Anton Blanchard -M: anton@samba.org -M: anton@au.ibm.com -W: http://www.penguinppc.org/ppc64/ -L: linuxppc-dev@ozlabs.org -S: Supported - LINUX SECURITY MODULE (LSM) FRAMEWORK P: Chris Wright M: chrisw@sous-sol.org -- cgit v0.10.2 From cde53535991fbb5c34a1566f25955297c1487b8d Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Fri, 4 Jul 2008 09:59:22 -0700 Subject: Christoph has moved Remove all clameter@sgi.com addresses from the kernel tree since they will become invalid on June 27th. Change my maintainer email address for the slab allocators to cl@linux-foundation.org (which will be the new email address for the future). Signed-off-by: Christoph Lameter Signed-off-by: Christoph Lameter Cc: Pekka Enberg Cc: Stephen Rothwell Cc: Matt Mackall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/Documentation/vm/slabinfo.c b/Documentation/vm/slabinfo.c index e4230ed..df32276 100644 --- a/Documentation/vm/slabinfo.c +++ b/Documentation/vm/slabinfo.c @@ -1,7 +1,7 @@ /* * Slabinfo: Tool to get reports about slabs * - * (C) 2007 sgi, Christoph Lameter + * (C) 2007 sgi, Christoph Lameter * * Compile by: * @@ -99,7 +99,7 @@ void fatal(const char *x, ...) void usage(void) { - printf("slabinfo 5/7/2007. (c) 2007 sgi. clameter@sgi.com\n\n" + printf("slabinfo 5/7/2007. (c) 2007 sgi.\n\n" "slabinfo [-ahnpvtsz] [-d debugopts] [slab-regexp]\n" "-a|--aliases Show aliases\n" "-A|--activity Most active slabs first\n" diff --git a/Documentation/vm/slub.txt b/Documentation/vm/slub.txt index 7c13f22..bb1f5c6 100644 --- a/Documentation/vm/slub.txt +++ b/Documentation/vm/slub.txt @@ -266,4 +266,4 @@ of other objects. slub_debug=FZ,dentry -Christoph Lameter, , May 30, 2007 +Christoph Lameter, May 30, 2007 diff --git a/MAINTAINERS b/MAINTAINERS index 460e699..13b7b19 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3672,7 +3672,7 @@ S: Maintained SLAB ALLOCATOR P: Christoph Lameter -M: clameter@sgi.com +M: cl@linux-foundation.org P: Pekka Enberg M: penberg@cs.helsinki.fi P: Matt Mackall diff --git a/include/asm-generic/atomic.h b/include/asm-generic/atomic.h index 85fd0aa..4ec0a29 100644 --- a/include/asm-generic/atomic.h +++ b/include/asm-generic/atomic.h @@ -2,7 +2,7 @@ #define _ASM_GENERIC_ATOMIC_H /* * Copyright (C) 2005 Silicon Graphics, Inc. - * Christoph Lameter + * Christoph Lameter * * Allows to provide arch independent atomic definitions without the need to * edit all arch specific atomic.h files. diff --git a/include/linux/slab.h b/include/linux/slab.h index c2ad350..9aa90a6 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -1,7 +1,7 @@ /* * Written by Mark Hemment, 1996 (markhe@nextd.demon.co.uk). * - * (C) SGI 2006, Christoph Lameter + * (C) SGI 2006, Christoph Lameter * Cleaned up and restructured to ease the addition of alternative * implementations of SLAB allocators. */ diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index cef6f8f..d117ea2 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -4,7 +4,7 @@ /* * SLUB : A Slab allocator without object queues. * - * (C) 2007 SGI, Christoph Lameter + * (C) 2007 SGI, Christoph Lameter */ #include #include diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 29fc39f..ce77995 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -13,7 +13,7 @@ * Kai Petzke * Theodore Ts'o * - * Made to use alloc_percpu by Christoph Lameter . + * Made to use alloc_percpu by Christoph Lameter. */ #include diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 169a2f8..56ec21a 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2001 Momchil Velikov * Portions Copyright (C) 2001 Christoph Hellwig - * Copyright (C) 2005 SGI, Christoph Lameter + * Copyright (C) 2005 SGI, Christoph Lameter * Copyright (C) 2006 Nick Piggin * * This program is free software; you can redistribute it and/or diff --git a/mm/allocpercpu.c b/mm/allocpercpu.c index f4026ba..05f2b40 100644 --- a/mm/allocpercpu.c +++ b/mm/allocpercpu.c @@ -1,7 +1,7 @@ /* * linux/mm/allocpercpu.c * - * Separated from slab.c August 11, 2006 Christoph Lameter + * Separated from slab.c August 11, 2006 Christoph Lameter */ #include #include diff --git a/mm/migrate.c b/mm/migrate.c index 112bcae..55bd355 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -9,7 +9,7 @@ * IWAMOTO Toshihiro * Hirokazu Takahashi * Dave Hansen - * Christoph Lameter + * Christoph Lameter */ #include diff --git a/mm/slub.c b/mm/slub.c index 2c9a62d..1a427c0 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5,7 +5,7 @@ * The allocator synchronizes using per slab locks and only * uses a centralized lock to manage a pool of partial slabs. * - * (C) 2007 SGI, Christoph Lameter + * (C) 2007 SGI, Christoph Lameter */ #include diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 99c4f36..a91b5f8 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -1,7 +1,7 @@ /* * Virtual Memory Map support * - * (C) 2007 sgi. Christoph Lameter . + * (C) 2007 sgi. Christoph Lameter. * * Virtual memory maps allow VM primitives pfn_to_page, page_to_pfn, * virt_to_page, page_address() to be implemented as a base offset -- cgit v0.10.2 From 251b97f552b1ad414cc5a9ccc8e4e94503edd5fc Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 4 Jul 2008 09:59:24 -0700 Subject: mm: dirty page accounting vs VM_MIXEDMAP Dirty page accounting accurately measures the amound of dirty pages in writable shared mappings by mapping the pages RO (as indicated by vma_wants_writenotify). We then trap on first write and call set_page_dirty() on the page, after which we map the page RW and continue execution. When we launder dirty pages, we call clear_page_dirty_for_io() which clears both the dirty flag, and maps the page RO again before we start writeout so that the story can repeat itself. vma_wants_writenotify() excludes VM_PFNMAP on the basis that we cannot do the regular dirty page stuff on raw PFNs and the memory isn't going anywhere anyway. The recently introduced VM_MIXEDMAP mixes both !pfn_valid() and pfn_valid() pages in a single mapping. We can't do dirty page accounting on !pfn_valid() pages as stated above, and mapping them RO causes them to be COW'ed on write, which breaks VM_SHARED semantics. Excluding VM_MIXEDMAP in vma_wants_writenotify() would mean we don't do the regular dirty page accounting for the pfn_valid() pages, which would bring back all the head-aches from inaccurate dirty page accounting. So instead, we let the !pfn_valid() pages get mapped RO, but fix them up unconditionally in the fault path. Signed-off-by: Peter Zijlstra Cc: Nick Piggin Acked-by: Hugh Dickins Cc: "Jared Hulbert" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/memory.c b/mm/memory.c index d14b251..350e646 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1697,8 +1697,19 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma, struct page *dirty_page = NULL; old_page = vm_normal_page(vma, address, orig_pte); - if (!old_page) + if (!old_page) { + /* + * VM_MIXEDMAP !pfn_valid() case + * + * We should not cow pages in a shared writeable mapping. + * Just mark the pages writable as we can't do any dirty + * accounting on raw pfn maps. + */ + if ((vma->vm_flags & (VM_WRITE|VM_SHARED)) == + (VM_WRITE|VM_SHARED)) + goto reuse; goto gotten; + } /* * Take out anonymous pages first, anonymous shared vmas are @@ -1751,6 +1762,7 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma, } if (reuse) { +reuse: flush_cache_page(vma, address, pte_pfn(orig_pte)); entry = pte_mkyoung(orig_pte); entry = maybe_mkwrite(pte_mkdirty(entry), vma); -- cgit v0.10.2 From a01cc6570326c01e70619bf6540fb32139947c33 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 4 Jul 2008 09:59:26 -0700 Subject: rtc: rtc_read_alarm() handles wraparound While 0e36a9a4a788e4e92407774df76c545910810d35 ("rtc: fix readback from /sys/class/rtc/rtc?/wakealarm") made sure that active alarms were never returned with invalid "wildcard" fields (negative), it can still report (wrongly) that the alarm triggers in the past. Example, if it's now 10am, an alarm firing at 5am will be triggered TOMORROW not today. (Which may also be next month or next year...) This updates that alarm handling in three ways: * Handle alarm rollover in the common cases of RTCs that don't support matching on all date fields. * Skip the invalid-field logic when it's not needed. * Minor bugfix ... tm_isdst should be ignored, it's one of the fields Linux doesn't maintain. A warning is emitted for some of the unhandled rollover cases, but the possible combinations are a bit too numerous to handle every bit of potential hardware and firmware braindamage. Signed-off-by: David Brownell Cc: Mark Lord Acked-by: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 7e3ad4f..58b7336 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -126,12 +126,25 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) int err; struct rtc_time before, now; int first_time = 1; + unsigned long t_now, t_alm; + enum { none, day, month, year } missing = none; + unsigned days; - /* The lower level RTC driver may not be capable of filling - * in all fields of the rtc_time struct (eg. rtc-cmos), - * and so might instead return -1 in some fields. - * We deal with that here by grabbing a current RTC timestamp - * and using values from that for any missing (-1) values. + /* The lower level RTC driver may return -1 in some fields, + * creating invalid alarm->time values, for reasons like: + * + * - The hardware may not be capable of filling them in; + * many alarms match only on time-of-day fields, not + * day/month/year calendar data. + * + * - Some hardware uses illegal values as "wildcard" match + * values, which non-Linux firmware (like a BIOS) may try + * to set up as e.g. "alarm 15 minutes after each hour". + * Linux uses only oneshot alarms. + * + * When we see that here, we deal with it by using values from + * a current RTC timestamp for any missing (-1) values. The + * RTC driver prevents "periodic alarm" modes. * * But this can be racey, because some fields of the RTC timestamp * may have wrapped in the interval since we read the RTC alarm, @@ -174,6 +187,10 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) if (!alarm->enabled) return 0; + /* full-function RTCs won't have such missing fields */ + if (rtc_valid_tm(&alarm->time) == 0) + return 0; + /* get the "after" timestamp, to detect wrapped fields */ err = rtc_read_time(rtc, &now); if (err < 0) @@ -183,22 +200,85 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) } while ( before.tm_min != now.tm_min || before.tm_hour != now.tm_hour || before.tm_mon != now.tm_mon - || before.tm_year != now.tm_year - || before.tm_isdst != now.tm_isdst); + || before.tm_year != now.tm_year); - /* Fill in any missing alarm fields using the timestamp */ + /* Fill in the missing alarm fields using the timestamp; we + * know there's at least one since alarm->time is invalid. + */ if (alarm->time.tm_sec == -1) alarm->time.tm_sec = now.tm_sec; if (alarm->time.tm_min == -1) alarm->time.tm_min = now.tm_min; if (alarm->time.tm_hour == -1) alarm->time.tm_hour = now.tm_hour; - if (alarm->time.tm_mday == -1) + + /* For simplicity, only support date rollover for now */ + if (alarm->time.tm_mday == -1) { alarm->time.tm_mday = now.tm_mday; - if (alarm->time.tm_mon == -1) + missing = day; + } + if (alarm->time.tm_mon == -1) { alarm->time.tm_mon = now.tm_mon; - if (alarm->time.tm_year == -1) + if (missing == none) + missing = month; + } + if (alarm->time.tm_year == -1) { alarm->time.tm_year = now.tm_year; + if (missing == none) + missing = year; + } + + /* with luck, no rollover is needed */ + rtc_tm_to_time(&now, &t_now); + rtc_tm_to_time(&alarm->time, &t_alm); + if (t_now < t_alm) + goto done; + + switch (missing) { + + /* 24 hour rollover ... if it's now 10am Monday, an alarm that + * that will trigger at 5am will do so at 5am Tuesday, which + * could also be in the next month or year. This is a common + * case, especially for PCs. + */ + case day: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "day"); + t_alm += 24 * 60 * 60; + rtc_time_to_tm(t_alm, &alarm->time); + break; + + /* Month rollover ... if it's the 31th, an alarm on the 3rd will + * be next month. An alarm matching on the 30th, 29th, or 28th + * may end up in the month after that! Many newer PCs support + * this type of alarm. + */ + case month: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "month"); + do { + if (alarm->time.tm_mon < 11) + alarm->time.tm_mon++; + else { + alarm->time.tm_mon = 0; + alarm->time.tm_year++; + } + days = rtc_month_days(alarm->time.tm_mon, + alarm->time.tm_year); + } while (days < alarm->time.tm_mday); + break; + + /* Year rollover ... easy except for leap years! */ + case year: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year"); + do { + alarm->time.tm_year++; + } while (!rtc_valid_tm(&alarm->time)); + break; + + default: + dev_warn(&rtc->dev, "alarm rollover not handled\n"); + } + +done: return 0; } EXPORT_SYMBOL_GPL(rtc_read_alarm); -- cgit v0.10.2 From 69d44a1835ec8163a82c4ee57367f87ae0f85c2e Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 4 Jul 2008 09:59:27 -0700 Subject: firmware: fix the request_firmware() dummy > the build (.config attached) failed, make ends with : > ... > UPD include/linux/compile.h > CC init/version.o > LD init/built-in.o > LD vmlinux > drivers/built-in.o: In function `sas_request_addr': > (.text+0x33bab): undefined reference to `request_firmware' > drivers/built-in.o: In function `sas_request_addr': > (.text+0x33c3f): undefined reference to `release_firmware' > make: *** [vmlinux] Error 1 There's a slight fault in the stub logic. It fails for FW_LOADER=m and the user =y. This should fix it. This patch fixes the following 2.6.26-rc regression: http://bugzilla.kernel.org/show_bug.cgi?id=10730 Reviewed-by: Toralf Foerster Signed-off-by: Adrian Bunk Cc: "Rafael J. Wysocki" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 4d10c73..6c7eff2 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -13,7 +13,7 @@ struct firmware { struct device; -#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) +#if defined(CONFIG_FW_LOADER) || (defined(CONFIG_FW_LOADER_MODULE) && defined(MODULE)) int request_firmware(const struct firmware **fw, const char *name, struct device *device); int request_firmware_nowait( -- cgit v0.10.2 From 7ca796f492a11f9408e661c8f22cd8c4f486b8e5 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 4 Jul 2008 09:59:28 -0700 Subject: serial: fix serial_match_port() for dynamic major tty-device numbers As reported by Vipul Gandhi, the current serial_match_port() doesn't work for tty-devices using dynamic major number allocation. Fix it. It oopses if you suspend a serial port with _dynamic_ major number. ATM, I think, there's only the drivers/serial/jsm/jsm_driver.c driver, that does it in-tree. Signed-off-by: Guennadi Liakhovetski Tested-by: Vipul Gandhi Cc: Alan Cox Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index c9b64e7..42d2e10 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -1991,7 +1991,9 @@ struct uart_match { static int serial_match_port(struct device *dev, void *data) { struct uart_match *match = data; - dev_t devt = MKDEV(match->driver->major, match->driver->minor) + match->port->line; + struct tty_driver *tty_drv = match->driver->tty_driver; + dev_t devt = MKDEV(tty_drv->major, tty_drv->minor_start) + + match->port->line; return dev->devt == devt; /* Actually, only one tty per port */ } -- cgit v0.10.2 From 7a36a752d006f6874049da510297eeb7f09d92a7 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 4 Jul 2008 09:59:28 -0700 Subject: get_user_pages(): fix possible page leak on oom get_user_pages() must not return the error when i != 0. When pages != NULL we have i get_page()'ed pages. Signed-off-by: Oleg Nesterov Acked-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/mm/memory.c b/mm/memory.c index 350e646..2302d22 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1151,7 +1151,7 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, * be processed until returning to user space. */ if (unlikely(test_tsk_thread_flag(tsk, TIF_MEMDIE))) - return -ENOMEM; + return i ? i : -ENOMEM; if (write) foll_flags |= FOLL_WRITE; -- cgit v0.10.2 From 471d47e3223311d2638755717f97dc9a298f6dc9 Mon Sep 17 00:00:00 2001 From: Michael Hamel Date: Fri, 4 Jul 2008 09:59:30 -0700 Subject: rtc-x1205: Fix alarm set I have discovered that the current version of rtc-x1205.c does not work correctly when asked to set the alarm time by the RTC_WKALM_SET ioctl() call. This happens because the alarm registers do not behave like the current-time registers. They are non-volatile. Two things go wrong: - the X1205 requires a 10 msec delay after any attempt to write to the non-volatile registers. The x1205_set_datetime() routine does the write as 8 single-byte writes without any delay. Only the first write succeeds. The second is NAKed because the chip is busy. - the X1205 resets the RWEL bit after any write to the non-volatile registers. This would lock out any further writes after the first even with a 10msec delay. I fix this by doing a single 8-byte write and then waiting 10msec for the chip to be ready. A side effect of this change is that it will speed up x1205_rtc_set_time() which uses the same code. I have also implemented the 'enable' bit in the rtc_wkalm structure, which the existing driver does not attempt to do. I have modified both x1205_rtc_set_alarm() to set the AL0E bit, and x1205_rtc_read_alarm() to return it. I have tested this patch on a LinkSys NSLU2 under OpenWRT, but on no other hardware. On the NSLU2 the X1205 correctly asserts its IRQ pin when the alarm time matches the current time. [akpm@linux-foundation.org: clean up over-parenthesisation] Signed-off-by: Michael Hamel Signed-off-by: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index eaf5594..7dcfba1 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -71,6 +71,7 @@ #define X1205_SR_RTCF 0x01 /* Clock failure */ #define X1205_SR_WEL 0x02 /* Write Enable Latch */ #define X1205_SR_RWEL 0x04 /* Register Write Enable */ +#define X1205_SR_AL0 0x20 /* Alarm 0 match */ #define X1205_DTR_DTR0 0x01 #define X1205_DTR_DTR1 0x02 @@ -78,6 +79,8 @@ #define X1205_HR_MIL 0x80 /* Set in ccr.hour for 24 hr mode */ +#define X1205_INT_AL0E 0x20 /* Alarm 0 enable */ + static struct i2c_driver x1205_driver; /* @@ -89,8 +92,8 @@ static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, unsigned char reg_base) { unsigned char dt_addr[2] = { 0, reg_base }; - unsigned char buf[8]; + int i; struct i2c_msg msgs[] = { { client->addr, 0, 2, dt_addr }, /* setup read ptr */ @@ -98,7 +101,7 @@ static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, }; /* read date registers */ - if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { dev_err(&client->dev, "%s: read error\n", __func__); return -EIO; } @@ -110,6 +113,11 @@ static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + /* Mask out the enable bits if these are alarm registers */ + if (reg_base < X1205_CCR_BASE) + for (i = 0; i <= 4; i++) + buf[i] &= 0x7F; + tm->tm_sec = BCD2BIN(buf[CCR_SEC]); tm->tm_min = BCD2BIN(buf[CCR_MIN]); tm->tm_hour = BCD2BIN(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */ @@ -138,7 +146,7 @@ static int x1205_get_status(struct i2c_client *client, unsigned char *sr) }; /* read status register */ - if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { dev_err(&client->dev, "%s: read error\n", __func__); return -EIO; } @@ -147,10 +155,11 @@ static int x1205_get_status(struct i2c_client *client, unsigned char *sr) } static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, - int datetoo, u8 reg_base) + int datetoo, u8 reg_base, unsigned char alm_enable) { - int i, xfer; + int i, xfer, nbytes; unsigned char buf[8]; + unsigned char rdata[10] = { 0, reg_base }; static const unsigned char wel[3] = { 0, X1205_REG_SR, X1205_SR_WEL }; @@ -189,6 +198,11 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, buf[CCR_Y2K] = BIN2BCD(tm->tm_year / 100); } + /* If writing alarm registers, set compare bits on registers 0-4 */ + if (reg_base < X1205_CCR_BASE) + for (i = 0; i <= 4; i++) + buf[i] |= 0x80; + /* this sequence is required to unlock the chip */ if ((xfer = i2c_master_send(client, wel, 3)) != 3) { dev_err(&client->dev, "%s: wel - %d\n", __func__, xfer); @@ -200,19 +214,57 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, return -EIO; } + /* write register's data */ - for (i = 0; i < (datetoo ? 8 : 3); i++) { - unsigned char rdata[3] = { 0, reg_base + i, buf[i] }; + if (datetoo) + nbytes = 8; + else + nbytes = 3; + for (i = 0; i < nbytes; i++) + rdata[2+i] = buf[i]; + + xfer = i2c_master_send(client, rdata, nbytes+2); + if (xfer != nbytes+2) { + dev_err(&client->dev, + "%s: result=%d addr=%02x, data=%02x\n", + __func__, + xfer, rdata[1], rdata[2]); + return -EIO; + } + + /* If we wrote to the nonvolatile region, wait 10msec for write cycle*/ + if (reg_base < X1205_CCR_BASE) { + unsigned char al0e[3] = { 0, X1205_REG_INT, 0 }; + + msleep(10); - xfer = i2c_master_send(client, rdata, 3); + /* ...and set or clear the AL0E bit in the INT register */ + + /* Need to set RWEL again as the write has cleared it */ + xfer = i2c_master_send(client, rwel, 3); if (xfer != 3) { dev_err(&client->dev, - "%s: xfer=%d addr=%02x, data=%02x\n", + "%s: aloe rwel - %d\n", __func__, - xfer, rdata[1], rdata[2]); + xfer); + return -EIO; + } + + if (alm_enable) + al0e[2] = X1205_INT_AL0E; + + xfer = i2c_master_send(client, al0e, 3); + if (xfer != 3) { + dev_err(&client->dev, + "%s: al0e - %d\n", + __func__, + xfer); return -EIO; } - }; + + /* and wait 10msec again for this write to complete */ + msleep(10); + } /* disable further writes */ if ((xfer = i2c_master_send(client, diswe, 3)) != 3) { @@ -230,9 +282,9 @@ static int x1205_fix_osc(struct i2c_client *client) tm.tm_hour = tm.tm_min = tm.tm_sec = 0; - if ((err = x1205_set_datetime(client, &tm, 0, X1205_CCR_BASE)) < 0) - dev_err(&client->dev, - "unable to restart the oscillator\n"); + err = x1205_set_datetime(client, &tm, 0, X1205_CCR_BASE, 0); + if (err < 0) + dev_err(&client->dev, "unable to restart the oscillator\n"); return err; } @@ -248,7 +300,7 @@ static int x1205_get_dtrim(struct i2c_client *client, int *trim) }; /* read dtr register */ - if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { dev_err(&client->dev, "%s: read error\n", __func__); return -EIO; } @@ -280,7 +332,7 @@ static int x1205_get_atrim(struct i2c_client *client, int *trim) }; /* read atr register */ - if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { dev_err(&client->dev, "%s: read error\n", __func__); return -EIO; } @@ -403,14 +455,33 @@ static int x1205_validate_client(struct i2c_client *client) static int x1205_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { - return x1205_get_datetime(to_i2c_client(dev), - &alrm->time, X1205_ALM0_BASE); + int err; + unsigned char intreg, status; + static unsigned char int_addr[2] = { 0, X1205_REG_INT }; + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msgs[] = { + { client->addr, 0, 2, int_addr }, /* setup read ptr */ + { client->addr, I2C_M_RD, 1, &intreg }, /* read INT register */ + }; + + /* read interrupt register and status register */ + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { + dev_err(&client->dev, "%s: read error\n", __func__); + return -EIO; + } + err = x1205_get_status(client, &status); + if (err == 0) { + alrm->pending = (status & X1205_SR_AL0) ? 1 : 0; + alrm->enabled = (intreg & X1205_INT_AL0E) ? 1 : 0; + err = x1205_get_datetime(client, &alrm->time, X1205_ALM0_BASE); + } + return err; } static int x1205_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { return x1205_set_datetime(to_i2c_client(dev), - &alrm->time, 1, X1205_ALM0_BASE); + &alrm->time, 1, X1205_ALM0_BASE, alrm->enabled); } static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm) @@ -422,7 +493,7 @@ static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm) static int x1205_rtc_set_time(struct device *dev, struct rtc_time *tm) { return x1205_set_datetime(to_i2c_client(dev), - tm, 1, X1205_CCR_BASE); + tm, 1, X1205_CCR_BASE, 0); } static int x1205_rtc_proc(struct device *dev, struct seq_file *seq) -- cgit v0.10.2 From cce3ce89c1abde1298dd0e769ab9c14ea95d7384 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Fri, 4 Jul 2008 09:59:31 -0700 Subject: rtc: fix CMOS time error after writing /proc/acpi/alarm When writing /proc/acpi/alarm in adjust mode, e.g. echo "+0000-00-00 00:00:15" >/proc/acpi/alarm The "century" field should be read and added to "year" field before writing, otherwise the CMOS time will go back to 2000 years ago, e.g. # cat /proc/acpi/alarm 0008-06-21 11:38:46 Then the system time may be reset to the date of manufacture after rebooting. This patch fixed this issue. Signed-off-by: Huacai Chen Acked-by: Pavel Machek Acked-by: Zhao Yakui Acked-by: Alessandro Zummo Acked-by: Len Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index 224c57c..4ebbba2 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -315,8 +315,11 @@ acpi_system_write_alarm(struct file *file, cmos_bcd_write(day, acpi_gbl_FADT.day_alarm, rtc_control); if (acpi_gbl_FADT.month_alarm) cmos_bcd_write(mo, acpi_gbl_FADT.month_alarm, rtc_control); - if (acpi_gbl_FADT.century) + if (acpi_gbl_FADT.century) { + if (adjust) + yr += cmos_bcd_read(acpi_gbl_FADT.century, rtc_control) * 100; cmos_bcd_write(yr / 100, acpi_gbl_FADT.century, rtc_control); + } /* enable the rtc alarm interrupt */ rtc_control |= RTC_AIE; CMOS_WRITE(rtc_control, RTC_CONTROL); -- cgit v0.10.2 From 66d715c95a39e84cd25204a665915621457d9691 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 4 Jul 2008 09:59:32 -0700 Subject: pci: VT3336 can't do MSI either It seems VT3336 can't do msi either as with its bro 3351. Disable it. Reported in the following SUSE bug. https://bugzilla.novell.com/show_bug.cgi?id=300001 Signed-off-by: Tejun Heo Acked-by: Jesse Barnes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index a3497dc..338a3f9 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1727,6 +1727,7 @@ static void __init quirk_disable_all_msi(struct pci_dev *dev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_GCNB_LE, quirk_disable_all_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS400_200, quirk_disable_all_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS480, quirk_disable_all_msi); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3336, quirk_disable_all_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3351, quirk_disable_all_msi); /* Disable MSI on chipsets that are known to not support it */ -- cgit v0.10.2 From 450c622e9ff19888818d4e2c4d31adb97a5242b2 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Fri, 4 Jul 2008 09:59:33 -0700 Subject: Miguel Ojeda has moved Signed-off-by: Miguel Ojeda Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/CREDITS b/CREDITS index 8fec7b3..e97bea0 100644 --- a/CREDITS +++ b/CREDITS @@ -2611,8 +2611,9 @@ S: Perth, Western Australia S: Australia N: Miguel Ojeda Sandonis -E: maxextreme@gmail.com -W: http://maxextreme.googlepages.com/ +E: miguel.ojeda.sandonis@gmail.com +W: http://miguelojeda.es +W: http://jair.lab.fi.uva.es/~migojed/ D: Author of the ks0108, cfag12864b and cfag12864bfb auxiliary display drivers. D: Maintainer of the auxiliary display drivers tree (drivers/auxdisplay/*) S: C/ Mieses 20, 9-B diff --git a/Documentation/auxdisplay/cfag12864b b/Documentation/auxdisplay/cfag12864b index b714183..eb7be39 100644 --- a/Documentation/auxdisplay/cfag12864b +++ b/Documentation/auxdisplay/cfag12864b @@ -3,7 +3,7 @@ =================================== License: GPLv2 -Author & Maintainer: Miguel Ojeda Sandonis +Author & Maintainer: Miguel Ojeda Sandonis Date: 2006-10-27 @@ -22,7 +22,7 @@ Date: 2006-10-27 1. DRIVER INFORMATION --------------------- -This driver support one cfag12864b display at time. +This driver supports a cfag12864b LCD. --------------------- diff --git a/Documentation/auxdisplay/cfag12864b-example.c b/Documentation/auxdisplay/cfag12864b-example.c index 7bfac35..2caeea5 100644 --- a/Documentation/auxdisplay/cfag12864b-example.c +++ b/Documentation/auxdisplay/cfag12864b-example.c @@ -4,7 +4,7 @@ * Description: cfag12864b LCD userspace example program * License: GPLv2 * - * Author: Copyright (C) Miguel Ojeda Sandonis + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-31 * * This program is free software; you can redistribute it and/or modify diff --git a/Documentation/auxdisplay/ks0108 b/Documentation/auxdisplay/ks0108 index 92b03b6..8ddda0c 100644 --- a/Documentation/auxdisplay/ks0108 +++ b/Documentation/auxdisplay/ks0108 @@ -3,7 +3,7 @@ ========================================== License: GPLv2 -Author & Maintainer: Miguel Ojeda Sandonis +Author & Maintainer: Miguel Ojeda Sandonis Date: 2006-10-27 @@ -21,7 +21,7 @@ Date: 2006-10-27 1. DRIVER INFORMATION --------------------- -This driver support the ks0108 LCD controller. +This driver supports the ks0108 LCD controller. --------------------- diff --git a/MAINTAINERS b/MAINTAINERS index 13b7b19..ba7ac13 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -763,9 +763,10 @@ S: Maintained AUXILIARY DISPLAY DRIVERS P: Miguel Ojeda Sandonis -M: maxextreme@gmail.com +M: miguel.ojeda.sandonis@gmail.com L: linux-kernel@vger.kernel.org -W: http://auxdisplay.googlepages.com/ +W: http://miguelojeda.es/auxdisplay.htm +W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm S: Maintained AVR32 ARCHITECTURE @@ -1055,16 +1056,18 @@ S: Supported CFAG12864B LCD DRIVER P: Miguel Ojeda Sandonis -M: maxextreme@gmail.com +M: miguel.ojeda.sandonis@gmail.com L: linux-kernel@vger.kernel.org -W: http://auxdisplay.googlepages.com/ +W: http://miguelojeda.es/auxdisplay.htm +W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm S: Maintained CFAG12864BFB LCD FRAMEBUFFER DRIVER P: Miguel Ojeda Sandonis -M: maxextreme@gmail.com +M: miguel.ojeda.sandonis@gmail.com L: linux-kernel@vger.kernel.org -W: http://auxdisplay.googlepages.com/ +W: http://miguelojeda.es/auxdisplay.htm +W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm S: Maintained CFG80211 and NL80211 @@ -2428,9 +2431,10 @@ S: Maintained KS0108 LCD CONTROLLER DRIVER P: Miguel Ojeda Sandonis -M: maxextreme@gmail.com +M: miguel.ojeda.sandonis@gmail.com L: linux-kernel@vger.kernel.org -W: http://auxdisplay.googlepages.com/ +W: http://miguelojeda.es/auxdisplay.htm +W: http://jair.lab.fi.uva.es/~migojed/auxdisplay.htm S: Maintained LAPB module diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index 043353b..14b9d5f 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -64,7 +64,7 @@ config KS0108_DELAY Amount of time the ks0108 should wait between each control write to the parallel port. - If your driver seems to miss random writings, increment this. + If your LCD seems to miss random writings, increment this. If you don't know what I'm talking about, ignore it. diff --git a/drivers/auxdisplay/cfag12864b.c b/drivers/auxdisplay/cfag12864b.c index 80bb061..683509f 100644 --- a/drivers/auxdisplay/cfag12864b.c +++ b/drivers/auxdisplay/cfag12864b.c @@ -5,7 +5,7 @@ * License: GPLv2 * Depends: ks0108 * - * Author: Copyright (C) Miguel Ojeda Sandonis + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-31 * * This program is free software; you can redistribute it and/or modify @@ -398,5 +398,5 @@ module_init(cfag12864b_init); module_exit(cfag12864b_exit); MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Miguel Ojeda Sandonis "); +MODULE_AUTHOR("Miguel Ojeda Sandonis "); MODULE_DESCRIPTION("cfag12864b LCD driver"); diff --git a/drivers/auxdisplay/cfag12864bfb.c b/drivers/auxdisplay/cfag12864bfb.c index 307c190..fe3a865 100644 --- a/drivers/auxdisplay/cfag12864bfb.c +++ b/drivers/auxdisplay/cfag12864bfb.c @@ -5,7 +5,7 @@ * License: GPLv2 * Depends: cfag12864b * - * Author: Copyright (C) Miguel Ojeda Sandonis + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-31 * * This program is free software; you can redistribute it and/or modify @@ -186,5 +186,5 @@ module_init(cfag12864bfb_init); module_exit(cfag12864bfb_exit); MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Miguel Ojeda Sandonis "); +MODULE_AUTHOR("Miguel Ojeda Sandonis "); MODULE_DESCRIPTION("cfag12864b LCD framebuffer driver"); diff --git a/drivers/auxdisplay/ks0108.c b/drivers/auxdisplay/ks0108.c index e6c3646..5b93852 100644 --- a/drivers/auxdisplay/ks0108.c +++ b/drivers/auxdisplay/ks0108.c @@ -5,7 +5,7 @@ * License: GPLv2 * Depends: parport * - * Author: Copyright (C) Miguel Ojeda Sandonis + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-31 * * This program is free software; you can redistribute it and/or modify @@ -173,6 +173,6 @@ module_init(ks0108_init); module_exit(ks0108_exit); MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Miguel Ojeda Sandonis "); +MODULE_AUTHOR("Miguel Ojeda Sandonis "); MODULE_DESCRIPTION("ks0108 LCD Controller driver"); diff --git a/include/linux/cfag12864b.h b/include/linux/cfag12864b.h index 1605dd8..6f9f19d 100644 --- a/include/linux/cfag12864b.h +++ b/include/linux/cfag12864b.h @@ -4,7 +4,7 @@ * Description: cfag12864b LCD driver header * License: GPLv2 * - * Author: Copyright (C) Miguel Ojeda Sandonis + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-12 * * This program is free software; you can redistribute it and/or modify diff --git a/include/linux/ks0108.h b/include/linux/ks0108.h index a2c54ac..cb31179 100644 --- a/include/linux/ks0108.h +++ b/include/linux/ks0108.h @@ -4,7 +4,7 @@ * Description: ks0108 LCD Controller driver header * License: GPLv2 * - * Author: Copyright (C) Miguel Ojeda Sandonis + * Author: Copyright (C) Miguel Ojeda Sandonis * Date: 2006-10-31 * * This program is free software; you can redistribute it and/or modify -- cgit v0.10.2 From f5c8f7dae75e1e6bb3200fc61302e4d5e2df3dc2 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 4 Jul 2008 09:59:33 -0700 Subject: ext3: add missing unlock to error path in ext3_quota_write() When write in ext3_quota_write() fails, we have to properly release i_mutex. One error path has been missing the unlock... Signed-off-by: Jan Kara Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/ext3/super.c b/fs/ext3/super.c index fe3119a..2845425 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -2875,8 +2875,10 @@ static ssize_t ext3_quota_write(struct super_block *sb, int type, blk++; } out: - if (len == towrite) + if (len == towrite) { + mutex_unlock(&inode->i_mutex); return err; + } if (inode->i_size < off+len-towrite) { i_size_write(inode, off+len-towrite); EXT3_I(inode)->i_disksize = inode->i_size; -- cgit v0.10.2 From 4d04e4fbf8fc9f5136a64d45e2c20de095c08efb Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 4 Jul 2008 09:59:34 -0700 Subject: ext4: add missing unlock to an error path in ext4_quota_write() When write in ext4_quota_write() fails, we have to properly release i_mutex. One error path has been missing the unlock... Signed-off-by: Jan Kara Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/ext4/super.c b/fs/ext4/super.c index cb96f127c..02bf243 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -3337,8 +3337,10 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type, blk++; } out: - if (len == towrite) + if (len == towrite) { + mutex_unlock(&inode->i_mutex); return err; + } if (inode->i_size < off+len-towrite) { i_size_write(inode, off+len-towrite); EXT4_I(inode)->i_disksize = inode->i_size; -- cgit v0.10.2 From 10dd08dc04c881dcc9f7f19e2a3ad8e0778e4db5 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 4 Jul 2008 09:59:34 -0700 Subject: reiserfs: add missing unlock to an error path in reiserfs_quota_write() When write in reiserfs_quota_write() fails, we have to properly release i_mutex. One error path has been missing the unlock... Signed-off-by: Jan Kara Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index ed424d7..1d40f2b 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -2165,8 +2165,10 @@ static ssize_t reiserfs_quota_write(struct super_block *sb, int type, blk++; } out: - if (len == towrite) + if (len == towrite) { + mutex_unlock(&inode->i_mutex); return err; + } if (inode->i_size < off + len - towrite) i_size_write(inode, off + len - towrite); inode->i_version++; -- cgit v0.10.2 From c4a2d7fbec3029c8891a3ad5fceec2992096a3b7 Mon Sep 17 00:00:00 2001 From: Michael Halcrow Date: Fri, 4 Jul 2008 09:59:35 -0700 Subject: ecryptfs: remove unnecessary mux from ecryptfs_init_ecryptfs_miscdev() The misc_mtx should provide all the protection required to keep the daemon hash table sane during miscdev registration. Since this mutex is causing gratuitous lockdep warnings, this patch removes it. Signed-off-by: Michael Halcrow Reported-by: Cyrill Gorcunov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/ecryptfs/miscdev.c b/fs/ecryptfs/miscdev.c index 50c994a..09a4522 100644 --- a/fs/ecryptfs/miscdev.c +++ b/fs/ecryptfs/miscdev.c @@ -575,13 +575,11 @@ int ecryptfs_init_ecryptfs_miscdev(void) int rc; atomic_set(&ecryptfs_num_miscdev_opens, 0); - mutex_lock(&ecryptfs_daemon_hash_mux); rc = misc_register(&ecryptfs_miscdev); if (rc) printk(KERN_ERR "%s: Failed to register miscellaneous device " "for communications with userspace daemons; rc = [%d]\n", __func__, rc); - mutex_unlock(&ecryptfs_daemon_hash_mux); return rc; } -- cgit v0.10.2 From da9eac8990dc614ab4756f2a3d84870b675f1f1e Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 4 Jul 2008 09:59:36 -0700 Subject: lib: taint kernel in common report_bug() WARN path. Commit 95b570c9cef3b12356454c7112571b7e406b4b51 ("Taint kernel after WARN_ON(condition)") introduced a TAINT_WARN that was implemented for all architectures using the generic warn_on_slowpath(), which excluded any architecture that set HAVE_ARCH_WARN_ON. As all of the architectures that implement their own WARN_ON() all go through the report_bug() path (specifically handling BUG_TRAP_TYPE_WARN), taint the kernel there as well for consistency. Tested on avr32 and sh. Also relevant for s390, parisc, and powerpc. Signed-off-by: Haavard Skinnemoen Signed-off-by: Paul Mundt Acked-by: Kyle McMartin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/lib/bug.c b/lib/bug.c index 530f38f..bfeafd6 100644 --- a/lib/bug.c +++ b/lib/bug.c @@ -37,6 +37,7 @@ */ #include #include +#include #include #include @@ -149,6 +150,7 @@ enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) (void *)bugaddr); show_regs(regs); + add_taint(TAINT_WARN); return BUG_TRAP_TYPE_WARN; } -- cgit v0.10.2 From 7059d4b08eba2ad046395a04b02e34ca27304d8f Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 4 Jul 2008 09:59:37 -0700 Subject: gpio: pca953x (i2c) handles max7310 too The pca953x driver can handle another 8-bit I/O expander, the max7310. This patch adds that chip to the list of supported IDs in that driver, and expands the Kconfig helptext accordingly. Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index bbd2834..008c38b 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -28,12 +28,18 @@ config DEBUG_GPIO comment "I2C GPIO expanders:" config GPIO_PCA953X - tristate "PCA953x I/O ports" + tristate "PCA953x, PCA955x, and MAX7310 I/O ports" depends on I2C help - Say yes here to support the PCA9534 (8-bit), PCA9535 (16-bit), - PCA9536 (4-bit), PCA9537 (4-bit), PCA9538 (8-bit), and PCA9539 - (16-bit) I/O ports. These parts are made by NXP and TI. + Say yes here to provide access to several register-oriented + SMBus I/O expanders, made mostly by NXP or TI. Compatible + models include: + + 4 bits: pca9536, pca9537 + + 8 bits: max7310, pca9534, pca9538, pca9554, pca9557 + + 16 bits: pca9535, pca9539, pca9555 This driver can also be built as a module. If so, the module will be called pca953x. diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c index 7e40e8a..a380730 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/pca953x.c @@ -33,7 +33,7 @@ static const struct i2c_device_id pca953x_id[] = { { "pca9554", 8, }, { "pca9555", 16, }, { "pca9557", 8, }, - /* REVISIT several pca955x parts should work here too */ + { "max7310", 8, }, { } }; MODULE_DEVICE_TABLE(i2c, pca953x_id); -- cgit v0.10.2 From 05946bce839b4fed5442dbfab77060fb75e051f3 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Fri, 4 Jul 2008 09:59:38 -0700 Subject: fsl_diu_fb: fix build with CONFIG_PM=y, plus fix some warnings This patch fixes following build error when CONFIG_PM is set. CC drivers/video/fsl-diu-fb.o drivers/video/fsl-diu-fb.c: In function 'fsl_diu_suspend': drivers/video/fsl-diu-fb.c:1327: error: 'ofdev' undeclared (first use in this function) drivers/video/fsl-diu-fb.c:1327: error: (Each undeclared identifier is reported only once drivers/video/fsl-diu-fb.c:1327: error: for each function it appears in.) drivers/video/fsl-diu-fb.c: In function 'fsl_diu_resume': drivers/video/fsl-diu-fb.c:1337: error: 'ofdev' undeclared (first use in this function) While I'm at it, also fix this warning: drivers/video/fsl-diu-fb.c: In function 'fsl_diu_alloc': drivers/video/fsl-diu-fb.c:314: warning: format '%lx' expects type 'long unsigned int', but argument 3 has type 'phys_addr_t' And these section mismatches: ..from the function fsl_diu_remove() to the function .exit.text:uninstall_fb() ..from the function fsl_diu_remove() to the function .exit.text:uninstall_fb() ..from the function install_fb() to the variable .devinit.data:fsl_diu_mode_db ..from the function install_fb() to the variable .devinit.data:fsl_diu_mode_db ..from the function fsl_diu_probe() to the function .exit.text:uninstall_fb() ..from the function fsl_diu_probe() to the function .exit.text:uninstall_fb() Also, some sparse fixes: make two functions static, and use NULL where appropriate. There are still a lot of sparse warnings, mainly wrt absence of __iomem annotations, but some will require ugly __force stuff. I'll leave them for now, since proper fix would be not that trivial as few one-liners below. Signed-off-by: Anton Vorontsov Cc: Timur Tabi Cc: Antonino Daplas Cc: York Sun Cc: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 0a27853..712dabc 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -286,7 +286,7 @@ static struct diu_pool pool; * rheap and make the furture large allocation fail. */ -void *fsl_diu_alloc(unsigned long size, phys_addr_t *phys) +static void *fsl_diu_alloc(unsigned long size, phys_addr_t *phys) { void *virt; @@ -311,12 +311,12 @@ void *fsl_diu_alloc(unsigned long size, phys_addr_t *phys) memset(virt, 0, size); } - pr_debug("rh virt=%p phys=%lx\n", virt, *phys); + pr_debug("rh virt=%p phys=%llx\n", virt, (unsigned long long)*phys); return virt; } -void fsl_diu_free(void *p, unsigned long size) +static void fsl_diu_free(void *p, unsigned long size) { pr_debug("p=%p size=%lu\n", p, size); @@ -770,7 +770,7 @@ static int map_video_memory(struct fb_info *info) info->fix.smem_len = info->fix.line_length * info->var.yres_virtual; pr_debug("MAP_VIDEO_MEMORY: smem_len = %d\n", info->fix.smem_len); info->screen_base = fsl_diu_alloc(info->fix.smem_len, &phys); - if (info->screen_base == 0) { + if (info->screen_base == NULL) { printk(KERN_ERR "Unable to allocate fb memory\n"); return -ENOMEM; } @@ -788,7 +788,7 @@ static int map_video_memory(struct fb_info *info) static void unmap_video_memory(struct fb_info *info) { fsl_diu_free(info->screen_base, info->fix.smem_len); - info->screen_base = 0; + info->screen_base = NULL; info->fix.smem_start = 0; info->fix.smem_len = 0; } @@ -1158,7 +1158,7 @@ static int init_fbinfo(struct fb_info *info) return 0; } -static int install_fb(struct fb_info *info) +static int __devinit install_fb(struct fb_info *info) { int rc; struct mfb_info *mfbi = info->par; @@ -1233,7 +1233,7 @@ static int install_fb(struct fb_info *info) return 0; } -static void __exit uninstall_fb(struct fb_info *info) +static void uninstall_fb(struct fb_info *info) { struct mfb_info *mfbi = info->par; @@ -1287,7 +1287,7 @@ static int request_irq_local(int irq) /* Read to clear the status */ status = in_be32(&hw->int_status); - ret = request_irq(irq, fsl_diu_isr, 0, "diu", 0); + ret = request_irq(irq, fsl_diu_isr, 0, "diu", NULL); if (ret) pr_info("Request diu IRQ failed.\n"); else { @@ -1312,7 +1312,7 @@ static void free_irq_local(int irq) /* Disable all LCDC interrupt */ out_be32(&hw->int_mask, 0x1f); - free_irq(irq, 0); + free_irq(irq, NULL); } #ifdef CONFIG_PM @@ -1324,7 +1324,7 @@ static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state) { struct fsl_diu_data *machine_data; - machine_data = dev_get_drvdata(&ofdev->dev); + machine_data = dev_get_drvdata(&dev->dev); disable_lcdc(machine_data->fsl_diu_info[0]); return 0; @@ -1334,7 +1334,7 @@ static int fsl_diu_resume(struct of_device *ofdev) { struct fsl_diu_data *machine_data; - machine_data = dev_get_drvdata(&ofdev->dev); + machine_data = dev_get_drvdata(&dev->dev); enable_lcdc(machine_data->fsl_diu_info[0]); return 0; @@ -1353,7 +1353,8 @@ static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align) dma_addr_t paddr = 0; ssize = size + bytes_align; - buf->vaddr = dma_alloc_coherent(0, ssize, &paddr, GFP_DMA | __GFP_ZERO); + buf->vaddr = dma_alloc_coherent(NULL, ssize, &paddr, GFP_DMA | + __GFP_ZERO); if (!buf->vaddr) return -ENOMEM; @@ -1371,7 +1372,7 @@ static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align) static void free_buf(struct diu_addr *buf, u32 size, u32 bytes_align) { - dma_free_coherent(0, size + bytes_align, + dma_free_coherent(NULL, size + bytes_align, buf->vaddr, (buf->paddr - buf->offset)); return; } @@ -1411,7 +1412,7 @@ static ssize_t show_monitor(struct device *device, return diu_ops.show_monitor_port(machine_data->monitor_port, buf); } -static int fsl_diu_probe(struct of_device *ofdev, +static int __devinit fsl_diu_probe(struct of_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->node; -- cgit v0.10.2 From f93f18cd94626108021c4e681f48d2d99bca5b90 Mon Sep 17 00:00:00 2001 From: Hiroshi Shimamoto Date: Fri, 4 Jul 2008 09:59:39 -0700 Subject: Update taskstats-struct document for scaled time accounting Update Documentation/accounting/taskstats-struct.txt for TASKSTATS_VERSION 6, adding scaled time accounting. Signed-off-by: Hiroshi Shimamoto Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/Documentation/accounting/taskstats-struct.txt b/Documentation/accounting/taskstats-struct.txt index 8aa7529..cd784f4 100644 --- a/Documentation/accounting/taskstats-struct.txt +++ b/Documentation/accounting/taskstats-struct.txt @@ -24,6 +24,8 @@ There are three different groups of fields in the struct taskstats: 4) Per-task and per-thread context switch count statistics +5) Time accounting for SMT machines + Future extension should add fields to the end of the taskstats struct, and should not change the relative position of each field within the struct. @@ -164,4 +166,8 @@ struct taskstats { __u64 nvcsw; /* Context voluntary switch counter */ __u64 nivcsw; /* Context involuntary switch counter */ +5) Time accounting for SMT machines + __u64 ac_utimescaled; /* utime scaled on frequency etc */ + __u64 ac_stimescaled; /* stime scaled on frequency etc */ + __u64 cpu_scaled_run_real_total; /* scaled cpu_run_real_total */ } -- cgit v0.10.2 From 77b96bd7e5ee0b44aed1b77fef5949bc19e8301f Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Fri, 4 Jul 2008 09:59:40 -0700 Subject: cciss: fix regression that no device nodes are created if no logical drives are configured. Fix regression in cciss driver that if no logical drives are configured, no device nodes at all get created. Signed-off-by: Stephen M. Cameron Acked-by: Mike Miller Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 5f1e1cc..f552105 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -3546,6 +3546,10 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, for (j = 0; j <= hba[i]->highest_lun; j++) add_disk(hba[i]->gendisk[j]); + /* we must register the controller even if no disks exist */ + if (hba[i]->highest_lun == -1) + add_disk(hba[i]->gendisk[0]); + return 1; clean4: -- cgit v0.10.2 From abc5f23a32919f3196d3fa22d5216ab192db236b Mon Sep 17 00:00:00 2001 From: Balbir Singh Date: Fri, 4 Jul 2008 09:59:42 -0700 Subject: delay accounting: maintainer update Update the delay accounting and taskstats maintainer to Balbir Singh. I spoke to Shailabh and he is now busy with other things. Cc: Shailabh Nagar Cc: Shailabh Nagar Signed-off-by: Balbir Singh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/MAINTAINERS b/MAINTAINERS index ba7ac13..64197ef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3183,8 +3183,8 @@ L: netdev@vger.kernel.org S: Maintained PER-TASK DELAY ACCOUNTING -P: Shailabh Nagar -M: nagar@watson.ibm.com +P: Balbir Singh +M: balbir@linux.vnet.ibm.com L: linux-kernel@vger.kernel.org S: Maintained @@ -3886,8 +3886,8 @@ M: hch@infradead.org S: Maintained TASKSTATS STATISTICS INTERFACE -P: Shailabh Nagar -M: nagar@watson.ibm.com +P: Balbir Singh +M: balbir@linux.vnet.ibm.com L: linux-kernel@vger.kernel.org S: Maintained -- cgit v0.10.2 From 71f77055deeab9708c00717352bec05aa125c713 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Fri, 4 Jul 2008 09:59:43 -0700 Subject: Doc*/kernel-parameters.txt: fix stale references Fix stale references to source files in kernel-parameters.txt. Signed-off-by: Pavel Machek Acked-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index e07c432..85c6f57 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -295,7 +295,7 @@ and is between 256 and 4096 characters. It is defined in the file when initialising the APIC and IO-APIC components. apm= [APM] Advanced Power Management - See header of arch/i386/kernel/apm.c. + See header of arch/x86/kernel/apm_32.c. arcrimi= [HW,NET] ARCnet - "RIM I" (entirely mem-mapped) cards Format: ,, @@ -638,7 +638,7 @@ and is between 256 and 4096 characters. It is defined in the file elanfreq= [X86-32] See comment before function elanfreq_setup() in - arch/i386/kernel/cpu/cpufreq/elanfreq.c. + arch/x86/kernel/cpu/cpufreq/elanfreq.c. elevator= [IOSCHED] Format: {"anticipatory" | "cfq" | "deadline" | "noop"} -- cgit v0.10.2 From 292d73551d0aa19526c3417e791c529b49ebadf3 Mon Sep 17 00:00:00 2001 From: maximilian attems Date: Fri, 4 Jul 2008 09:59:43 -0700 Subject: hdaps: add support for various newer Lenovo thinkpads Adds R61, T61p, X61s, X61, Z61m, Z61p models to whitelist. Fixes this: cullen@lenny:~$ sudo modprobe hdaps FATAL: Error inserting hdaps (/lib/modules/2.6.22-10-generic/kernel/drivers/hwmon/hdaps.ko): No such device [25192.888000] hdaps: supported laptop not found! [25192.888000] hdaps: driver init failed (ret=-19)! Originally based on an Ubuntu patch that got it wrong, the dmidecode output of the corresponding laptops shows LENOVO as the manufacturer. https://bugs.launchpad.net/ubuntu/+source/linux-source-2.6.22/+bug/133636 tested on X61s: [ 184.893588] hdaps: inverting axis readings. [ 184.893588] hdaps: LENOVO ThinkPad X61s detected. [ 184.893588] input: hdaps as /class/input/input12 [ 184.924326] hdaps: driver successfully loaded. Cc: Klaus S. Madsen Cc: Chuck Short Cc: Jean Delvare Cc: Tim Gardner Signed-off-by: maximilian attems Cc: Mark M. Hoffman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c index 26df06f..50f2269 100644 --- a/drivers/hwmon/hdaps.c +++ b/drivers/hwmon/hdaps.c @@ -516,17 +516,23 @@ static struct dmi_system_id __initdata hdaps_whitelist[] = { HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61"), HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"), HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X41"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p"), { .ident = NULL } }; -- cgit v0.10.2 From 7fc7228c0be9007f0e6a32c8a8ae340ea6246056 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 Jul 2008 09:59:46 -0700 Subject: mn10300: export certain arch symbols required to build allmodconfig Export kernel_thread() and empty_zero_page so that allmodconfig can be built for MN10300. Signed-off-by: David Howells Cc: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/mn10300/kernel/mn10300_ksyms.c b/arch/mn10300/kernel/mn10300_ksyms.c index 6d19628..544ee1a 100644 --- a/arch/mn10300/kernel/mn10300_ksyms.c +++ b/arch/mn10300/kernel/mn10300_ksyms.c @@ -10,8 +10,11 @@ */ #include #include +#include +EXPORT_SYMBOL(empty_zero_page); + EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit); diff --git a/arch/mn10300/kernel/process.c b/arch/mn10300/kernel/process.c index 9c623c8..b28c9a6 100644 --- a/arch/mn10300/kernel/process.c +++ b/arch/mn10300/kernel/process.c @@ -153,6 +153,7 @@ int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); } +EXPORT_SYMBOL(kernel_thread); /* * free current thread data structures etc.. -- cgit v0.10.2 From fc26361ef0fbcad0406475fc6006fa4f09e60dce Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 Jul 2008 09:59:47 -0700 Subject: mn10300: provide __ucmpdi2() for MN10300 Provide __ucmpdi2() for MN10300 so that allmodconfig can be built. Signed-off-by: David Howells Cc: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/mn10300/kernel/mn10300_ksyms.c b/arch/mn10300/kernel/mn10300_ksyms.c index 544ee1a..f9eb975 100644 --- a/arch/mn10300/kernel/mn10300_ksyms.c +++ b/arch/mn10300/kernel/mn10300_ksyms.c @@ -34,7 +34,9 @@ extern u64 __ashrdi3(u64, unsigned); extern u64 __ashldi3(u64, unsigned); extern u64 __lshrdi3(u64, unsigned); extern s64 __negdi2(s64); +extern int __ucmpdi2(u64, u64); EXPORT_SYMBOL(__ashrdi3); EXPORT_SYMBOL(__ashldi3); EXPORT_SYMBOL(__lshrdi3); EXPORT_SYMBOL(__negdi2); +EXPORT_SYMBOL(__ucmpdi2); diff --git a/arch/mn10300/lib/Makefile b/arch/mn10300/lib/Makefile index fdfa9ec..0cd2346 100644 --- a/arch/mn10300/lib/Makefile +++ b/arch/mn10300/lib/Makefile @@ -4,4 +4,4 @@ lib-y = delay.o usercopy.o checksum.o bitops.o memcpy.o memmove.o memset.o lib-y += do_csum.o -lib-y += __ashldi3.o __ashrdi3.o __lshrdi3.o negdi2.o +lib-y += __ashldi3.o __ashrdi3.o __lshrdi3.o negdi2.o __ucmpdi2.o diff --git a/arch/mn10300/lib/__ucmpdi2.S b/arch/mn10300/lib/__ucmpdi2.S new file mode 100644 index 0000000..60dcbdf --- /dev/null +++ b/arch/mn10300/lib/__ucmpdi2.S @@ -0,0 +1,43 @@ +/* __ucmpdi2.S: 64-bit unsigned compare + * + * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + + + .text + .p2align 4 + +############################################################################### +# +# int __ucmpdi2(unsigned long long a [D0:D1], +# unsigned long long b [(SP,12),(SP,16)]) +# +# - returns 0, 1, or 2 as a <, =, > b respectively. +# +############################################################################### + .globl __ucmpdi2 + .type __ucmpdi2,@function +__ucmpdi2: + mov (12,sp),a0 # b.lsw + mov (16,sp),a1 # b.msw + + sub a0,d0 + subc a1,d1 # may clear Z, never sets it + bne __ucmpdi2_differ # a.msw != b.msw + mov +1,d0 + rets + +__ucmpdi2_differ: + # C flag is set if LE, clear if GE + subc d0,d0 # -1 if LE, 0 if GE + add +1,d0 # 0 if LE, 1 if GE + add d0,d0 # 0 if LE, 2 if GE + rets + + .size __ucmpdi2, .-__ucmpdi2 -- cgit v0.10.2 From 93921f5c2ce7427cc30341c86882527d1d1d8770 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Fri, 4 Jul 2008 09:59:48 -0700 Subject: Introduce rculist.h In linux-next there is a commit ("rcu: split list.h and move rcu-protected lists into rculist.h") that moved the rcu related list iterators from list.h to rculist.h. Add a trivial version of the file now so that various subsystem trees can start using it now for -next changes and so reduce the build errors caused by adding uses of the moved functions. Cc: Franck Bui-Huu Acked-by: Paul E. McKenney Cc: Josh Triplett Acked-by: Ingo Molnar Signed-off-by: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/include/linux/rculist.h b/include/linux/rculist.h new file mode 100644 index 0000000..bde4586 --- /dev/null +++ b/include/linux/rculist.h @@ -0,0 +1,6 @@ +#ifndef _LINUX_RCULIST_H +#define _LINUX_RCULIST_H + +#include + +#endif /* _LINUX_RCULIST_H */ -- cgit v0.10.2 From c76a58783d7bb2190872c5431e5ee44da7143fad Mon Sep 17 00:00:00 2001 From: Michael Kerrisk Date: Fri, 4 Jul 2008 09:59:49 -0700 Subject: man-pages is supported Starting last month, I reached a long-time goal: man-pages finally has a paid, full-time maintainer, thanks to a fellowship from the Linux Foundation. It's still a little unclear how long the LF money will last for the fellowship, but for the foreseeable future, I'll be working on: * Properly documenting every new Linux kernel-userland (and glibc) API, and every API change, that is released into the mainline kernel, ideally before actual release. (That's the ideal, but there's a quite a backlog, so I'm not going to achieve the ideal immediately.) * Testing new APIs, again ideally before they are released into the mainline kernel, and probably doing some light bug fixing while I'm at it (e.g., the recent utimensat() work). * Design review of new APIs, which of course can only usefully be done before they are released into the mainline kernel. * And of course accepting patches and dealing with bug reports for existing man pages. Signed-off-by: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/MAINTAINERS b/MAINTAINERS index 64197ef..049ff31 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2668,8 +2668,8 @@ S: Supported MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7 P: Michael Kerrisk M: mtk.manpages@gmail.com -W: ftp://ftp.kernel.org/pub/linux/docs/manpages -S: Maintained +W: http://www.kernel.org/doc/man-pages +S: Supported MARVELL LIBERTAS WIRELESS DRIVER P: Dan Williams -- cgit v0.10.2 From 337e2ab5d1efca56c6fdd57bffaea7e7899e7283 Mon Sep 17 00:00:00 2001 From: Jess Guerrero Date: Fri, 4 Jul 2008 09:59:50 -0700 Subject: ntfs: update help text The url in the help text for ntfs should be updated. Acked-by: Anton Altaparmakov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/Kconfig b/fs/Kconfig index cf12c40..2694648 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -830,7 +830,7 @@ config NTFS_FS from the project web site. For more information see - and . + and . To compile this file system support as a module, choose M here: the module will be called ntfs. -- cgit v0.10.2 From 6d1029b56329b1cc9b7233e5333c1a48ddbbfad8 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 4 Jul 2008 09:59:51 -0700 Subject: add kernel-doc for simple_read_from_buffer and memory_read_from_buffer Add kernel-doc comments describing simple_read_from_buffer and memory_read_from_buffer. Signed-off-by: Akinobu Mita Cc: Christoph Hellwig Cc: "Randy.Dunlap" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/libfs.c b/fs/libfs.c index 892d41c..baeb71e 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -512,6 +512,20 @@ void simple_release_fs(struct vfsmount **mount, int *count) mntput(mnt); } +/** + * simple_read_from_buffer - copy data from the buffer to user space + * @to: the user space buffer to read to + * @count: the maximum number of bytes to read + * @ppos: the current position in the buffer + * @from: the buffer to read from + * @available: the size of the buffer + * + * The simple_read_from_buffer() function reads up to @count bytes from the + * buffer @from at offset @ppos into the user space address starting at @to. + * + * On success, the number of bytes read is returned and the offset @ppos is + * advanced by this number, or negative value is returned on error. + **/ ssize_t simple_read_from_buffer(void __user *to, size_t count, loff_t *ppos, const void *from, size_t available) { @@ -528,6 +542,20 @@ ssize_t simple_read_from_buffer(void __user *to, size_t count, loff_t *ppos, return count; } +/** + * memory_read_from_buffer - copy data from the buffer + * @to: the kernel space buffer to read to + * @count: the maximum number of bytes to read + * @ppos: the current position in the buffer + * @from: the buffer to read from + * @available: the size of the buffer + * + * The memory_read_from_buffer() function reads up to @count bytes from the + * buffer @from at offset @ppos into the kernel space address starting at @to. + * + * On success, the number of bytes read is returned and the offset @ppos is + * advanced by this number, or negative value is returned on error. + **/ ssize_t memory_read_from_buffer(void *to, size_t count, loff_t *ppos, const void *from, size_t available) { -- cgit v0.10.2 From e5dd3cbd81aad69bdf773ab63c06fbaabc2b767a Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 4 Jul 2008 09:59:52 -0700 Subject: w100fb: do not depend on SHARPSL Apart from Sharp SL-Cxx series, there are a few other devices that have ATI Imageon chips, among them HP iPAQ hx4700. Signed-off-by: Philipp Zabel Acked-by: Ian Molton Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 002b61b..e0c5f96 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1825,12 +1825,13 @@ config FB_FSL_DIU config FB_W100 tristate "W100 frame buffer support" - depends on FB && PXA_SHARPSL + depends on FB && ARCH_PXA select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT ---help--- Frame buffer driver for the w100 as found on the Sharp SL-Cxx series. + It can also drive the w3220 chip found on iPAQ hx4700. This driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). The -- cgit v0.10.2 From 27c8d95f8c9ff83e4e4d8a90523d891427964c79 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 4 Jul 2008 09:59:53 -0700 Subject: w100fb: add 80 MHz modeline This is needed for HTC Blueangel (w3200). At 96MHz its screen flickers. Signed-off-by: Philipp Zabel Acked-by: Ian Molton Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/video/w100fb.c b/drivers/video/w100fb.c index 30469bf..d0674f1 100644 --- a/drivers/video/w100fb.c +++ b/drivers/video/w100fb.c @@ -1003,6 +1003,7 @@ static struct w100_pll_info xtal_14318000[] = { static struct w100_pll_info xtal_16000000[] = { /*freq M N_int N_fac tfgoal lock_time */ { 72, 1, 8, 0, 0xe0, 48}, /* tfgoal guessed */ + { 80, 1, 9, 0, 0xe0, 13}, /* tfgoal guessed */ { 95, 1, 10, 7, 0xe0, 38}, /* tfgoal guessed */ { 96, 1, 11, 0, 0xe0, 36}, /* tfgoal guessed */ { 0, 0, 0, 0, 0, 0}, -- cgit v0.10.2 From 4e0d13cbb71de2b1baf71ddd3c14f7ba8da810e5 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 4 Jul 2008 09:59:53 -0700 Subject: MFD maintainer We probably need someone to look after the few drivers/mfd patches coming every now and then. As agreed with Andrew, I'm ok to do so and my employer is fine with me spending a few working hours on it, if needed. Ben, Philipp, feel free to add your names there too if you wish. Signed-off-by: Samuel Ortiz Cc: "pHilipp Zabel" Cc: Ian Molton Cc: Ben Dooks Cc: Dmitry Baryshkov Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/MAINTAINERS b/MAINTAINERS index 049ff31..f13f82f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2802,6 +2802,12 @@ W: https://tango.0pointer.de/mailman/listinfo/s270-linux W: http://0pointer.de/lennart/tchibo.html S: Maintained +MULTIFUNCTION DEVICES (MFD) +P: Samuel Ortiz +M: sameo@openedhand.com +L: linux-kernel@vger.kernel.org +S: Supported + MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM P: Pierre Ossman M: drzeus-mmc@drzeus.cx -- cgit v0.10.2 From bef67c5a7d3a9c45e091e36625c09c0c811e2672 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Fri, 4 Jul 2008 09:59:55 -0700 Subject: cgroups: document the effect of attaching PID 0 to a cgroup Document that a pid of zero(0) can be used to refer to the current task when attaching a task to a cgroup, as in the following usage: # echo 0 > /dev/cgroup/tasks This is consistent with existing cpuset behavior. Signed-off-by: Li Zefan Acked-by: Paul Jackson Acked-by: Dhaval Giani Cc: Paul Menage Cc: Balbir Singh Cc: Andrea Righi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/Documentation/cgroups.txt b/Documentation/cgroups.txt index 824fc02..d9014aa 100644 --- a/Documentation/cgroups.txt +++ b/Documentation/cgroups.txt @@ -390,6 +390,10 @@ If you have several tasks to attach, you have to do it one after another: ... # /bin/echo PIDn > tasks +You can attach the current shell task by echoing 0: + +# echo 0 > tasks + 3. Kernel API ============= -- cgit v0.10.2 From 4b1295b0df28cffd40e6c6d7c4b88dec7af1eb76 Mon Sep 17 00:00:00 2001 From: Sebastian Siewior Date: Fri, 4 Jul 2008 09:59:56 -0700 Subject: spi: fix the read path in spidev This got broken by the recent "fix rmmod $spi_driver while spidev-user is active". I tested the rmmod & write path but didn't check the read path. I am sorry. The read logic changed and spidev_sync_read() + spidev_sync_write() do not return zero on success anymore but the number of bytes that has been transfered over the bus. This patch changes the logic and copy_to_user() gets called again. The write path returns the number of bytes which are written to the underlying device what may be less than the requested size. This patch makes the same change to the read path or else we request a read of 20 bytes, get 10, don't call copy to user and report to the user that we read 10 bytes. [akpm@linux-foundation.org: remove test of known-to-be-zero local] Signed-off-by: Sebastian Siewior Acked-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 799337f..f5b60c7 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -167,14 +167,14 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) mutex_lock(&spidev->buf_lock); status = spidev_sync_read(spidev, count); - if (status == 0) { + if (status > 0) { unsigned long missing; - missing = copy_to_user(buf, spidev->buffer, count); - if (count && missing == count) + missing = copy_to_user(buf, spidev->buffer, status); + if (missing == status) status = -EFAULT; else - status = count - missing; + status = status - missing; } mutex_unlock(&spidev->buf_lock); @@ -200,8 +200,6 @@ spidev_write(struct file *filp, const char __user *buf, missing = copy_from_user(spidev->buffer, buf, count); if (missing == 0) { status = spidev_sync_write(spidev, count); - if (status == 0) - status = count; } else status = -EFAULT; mutex_unlock(&spidev->buf_lock); -- cgit v0.10.2 From abbaeff38c00cb7f6817ec1cef406b27081ebedd Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 4 Jul 2008 09:59:57 -0700 Subject: doc: doc maintainers Maintain the kernel's Documentation/ tree. This includes tree layout and contents, although not much in terms of new content production. That will usually have to be done by someone familiar with the software, at least in some rough form. Includes review and editorial assistance for people contributing changes to /Documentation. Also includes prodding people for content if something is in need of documentation. Signed-off-by: Randy Dunlap Acked-by: Michael Kerrisk Cc: Jonathan Corbet Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/MAINTAINERS b/MAINTAINERS index f13f82f..8c4ad23 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1423,6 +1423,14 @@ M: kristen.c.accardi@intel.com L: linux-acpi@vger.kernel.org S: Supported +DOCUMENTATION (/Documentation directory) +P: Michael Kerrisk +M: mtk.manpages@gmail.com +P: Randy Dunlap +M: rdunlap@xenotime.net +L: linux-doc@vger.kernel.org +S: Maintained + DOUBLETALK DRIVER P: James R. Van Zandt M: jrv@vanzandt.mv.com -- cgit v0.10.2 From 086f7316f0d400806d76323beefae996bb3849b1 Mon Sep 17 00:00:00 2001 From: "Andrew G. Morgan" Date: Fri, 4 Jul 2008 09:59:58 -0700 Subject: security: filesystem capabilities: fix fragile setuid fixup code This commit includes a bugfix for the fragile setuid fixup code in the case that filesystem capabilities are supported (in access()). The effect of this fix is gated on filesystem capability support because changing securebits is only supported when filesystem capabilities support is configured.) [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Andrew G. Morgan Acked-by: Serge Hallyn Acked-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/open.c b/fs/open.c index a145008..a99ad09 100644 --- a/fs/open.c +++ b/fs/open.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -425,7 +426,7 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) { struct nameidata nd; int old_fsuid, old_fsgid; - kernel_cap_t old_cap; + kernel_cap_t uninitialized_var(old_cap); /* !SECURE_NO_SETUID_FIXUP */ int res; if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ @@ -433,23 +434,27 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) old_fsuid = current->fsuid; old_fsgid = current->fsgid; - old_cap = current->cap_effective; current->fsuid = current->uid; current->fsgid = current->gid; - /* - * Clear the capabilities if we switch to a non-root user - * - * FIXME: There is a race here against sys_capset. The - * capabilities can change yet we will restore the old - * value below. We should hold task_capabilities_lock, - * but we cannot because user_path_walk can sleep. - */ - if (current->uid) - cap_clear(current->cap_effective); - else - current->cap_effective = current->cap_permitted; + if (!issecure(SECURE_NO_SETUID_FIXUP)) { + /* + * Clear the capabilities if we switch to a non-root user + */ +#ifndef CONFIG_SECURITY_FILE_CAPABILITIES + /* + * FIXME: There is a race here against sys_capset. The + * capabilities can change yet we will restore the old + * value below. We should hold task_capabilities_lock, + * but we cannot because user_path_walk can sleep. + */ +#endif /* ndef CONFIG_SECURITY_FILE_CAPABILITIES */ + if (current->uid) + old_cap = cap_set_effective(__cap_empty_set); + else + old_cap = cap_set_effective(current->cap_permitted); + } res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd); if (res) @@ -478,7 +483,9 @@ out_path_release: out: current->fsuid = old_fsuid; current->fsgid = old_fsgid; - current->cap_effective = old_cap; + + if (!issecure(SECURE_NO_SETUID_FIXUP)) + cap_set_effective(old_cap); return res; } diff --git a/include/linux/capability.h b/include/linux/capability.h index fa830f8..0267384 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -501,6 +501,8 @@ extern const kernel_cap_t __cap_empty_set; extern const kernel_cap_t __cap_full_set; extern const kernel_cap_t __cap_init_eff_set; +kernel_cap_t cap_set_effective(const kernel_cap_t pE_new); + int capable(int cap); int __capable(struct task_struct *t, int cap); diff --git a/include/linux/securebits.h b/include/linux/securebits.h index c1f19db..92f09bd 100644 --- a/include/linux/securebits.h +++ b/include/linux/securebits.h @@ -7,14 +7,15 @@ inheritance of root-permissions and suid-root executable under compatibility mode. We raise the effective and inheritable bitmasks *of the executable file* if the effective uid of the new process is - 0. If the real uid is 0, we raise the inheritable bitmask of the + 0. If the real uid is 0, we raise the effective (legacy) bit of the executable file. */ #define SECURE_NOROOT 0 #define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */ -/* When set, setuid to/from uid 0 does not trigger capability-"fixes" - to be compatible with old programs relying on set*uid to loose - privileges. When unset, setuid doesn't change privileges. */ +/* When set, setuid to/from uid 0 does not trigger capability-"fixup". + When unset, to provide compatiblility with old programs relying on + set*uid to gain/lose privilege, transitions to/from uid 0 cause + capabilities to be gained/lost. */ #define SECURE_NO_SETUID_FIXUP 2 #define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */ @@ -26,10 +27,10 @@ #define SECURE_KEEP_CAPS 4 #define SECURE_KEEP_CAPS_LOCKED 5 /* make bit-4 immutable */ -/* Each securesetting is implemented using two bits. One bit specify +/* Each securesetting is implemented using two bits. One bit specifies whether the setting is on or off. The other bit specify whether the - setting is fixed or not. A setting which is fixed cannot be changed - from user-level. */ + setting is locked or not. A setting which is locked cannot be + changed from user-level. */ #define issecure_mask(X) (1 << (X)) #define issecure(X) (issecure_mask(X) & current->securebits) diff --git a/kernel/capability.c b/kernel/capability.c index cfbe442..901e0fd 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -121,6 +121,27 @@ static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy) * uninteresting and/or not to be changed. */ +/* + * Atomically modify the effective capabilities returning the original + * value. No permission check is performed here - it is assumed that the + * caller is permitted to set the desired effective capabilities. + */ +kernel_cap_t cap_set_effective(const kernel_cap_t pE_new) +{ + kernel_cap_t pE_old; + + spin_lock(&task_capability_lock); + + pE_old = current->cap_effective; + current->cap_effective = pE_new; + + spin_unlock(&task_capability_lock); + + return pE_old; +} + +EXPORT_SYMBOL(cap_set_effective); + /** * sys_capget - get the capabilities of a given process. * @header: pointer to struct that contains capability version and -- cgit v0.10.2 From 1209726ce942047c9fefe7cd427dc36f8e9ded53 Mon Sep 17 00:00:00 2001 From: "Andrew G. Morgan" Date: Fri, 4 Jul 2008 09:59:59 -0700 Subject: security: filesystem capabilities: fix CAP_SETPCAP handling The filesystem capability support meaning for CAP_SETPCAP is less powerful than the non-filesystem capability support. As such, when filesystem capabilities are configured, we should not permit CAP_SETPCAP to 'enhance' the current process through strace manipulation of a child process. Signed-off-by: Andrew G. Morgan Acked-by: Serge Hallyn Cc: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/security/commoncap.c b/security/commoncap.c index 5edabc7..33d3433 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -103,10 +103,16 @@ static inline int cap_inh_is_capped(void) return (cap_capable(current, CAP_SETPCAP) != 0); } +static inline int cap_limit_ptraced_target(void) { return 1; } + #else /* ie., ndef CONFIG_SECURITY_FILE_CAPABILITIES */ static inline int cap_block_setpcap(struct task_struct *t) { return 0; } static inline int cap_inh_is_capped(void) { return 1; } +static inline int cap_limit_ptraced_target(void) +{ + return !capable(CAP_SETPCAP); +} #endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */ @@ -342,9 +348,10 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) bprm->e_uid = current->uid; bprm->e_gid = current->gid; } - if (!capable (CAP_SETPCAP)) { - new_permitted = cap_intersect (new_permitted, - current->cap_permitted); + if (cap_limit_ptraced_target()) { + new_permitted = + cap_intersect(new_permitted, + current->cap_permitted); } } } -- cgit v0.10.2 From 51597acfd3c09073aeea94a0e6f76a931f8c22d2 Mon Sep 17 00:00:00 2001 From: Bastian Blank Date: Fri, 4 Jul 2008 10:00:00 -0700 Subject: Alpha Linux kernel fails with inconsistent kallsyms data The build of the Alpha Linux kernel currently fails[1] with inconsistent kallsyms data. As I never saw that before, I thought about hardware problems. But in fact it is a bug in the Linux kernel. The end of the rodata section is marked with the "__end_rodata" symbol. This symbol have different aligning constraints than the inittext parts and therefor the start marked "_sinittext". Because of that the __end_rodata symbol shifts between < _sinittext and == _sinittext. The later variant is seen as a code symbol and recorded in the kallsyms data. On fix would be to move the exception table a little bit and get some space between that two areas. [1]: http://buildd.debian.org/fetch.cgi?pkg=linux-2.6&arch=alpha&ver=2.6.25-5&stamp=1213919009&file=log&as=raw Cc: maximilian attems Cc: Richard Henderson Cc: Ivan Kokshaysky Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/arch/alpha/kernel/vmlinux.lds.S b/arch/alpha/kernel/vmlinux.lds.S index f13249b..ef37fc1 100644 --- a/arch/alpha/kernel/vmlinux.lds.S +++ b/arch/alpha/kernel/vmlinux.lds.S @@ -25,6 +25,13 @@ SECTIONS } :kernel _etext = .; /* End of text section */ + NOTES :kernel :note + .dummy : { + *(.dummy) + } :kernel + + RODATA + /* Exception table */ . = ALIGN(16); __ex_table : { @@ -33,13 +40,6 @@ SECTIONS __stop___ex_table = .; } - NOTES :kernel :note - .dummy : { - *(.dummy) - } :kernel - - RODATA - /* Will be freed after init */ . = ALIGN(PAGE_SIZE); /* Init code and data */ -- cgit v0.10.2 From 985ee7f224cca8a0d622b24ec399f364c63fc274 Mon Sep 17 00:00:00 2001 From: Paul Jackson Date: Fri, 4 Jul 2008 10:00:01 -0700 Subject: cpusets: document proc status cpus and mems allowed lists Provide a little documentation for the two new fields, Cpus_allowed_list and Mems_allowed_list, that were added to each /proc//status file a while back. Signed-off-by: Paul Jackson Acked-by: Michael Kerrisk Cc: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/Documentation/cpusets.txt b/Documentation/cpusets.txt index 353504d..894b844 100644 --- a/Documentation/cpusets.txt +++ b/Documentation/cpusets.txt @@ -154,13 +154,15 @@ browsing and modifying the cpusets presently known to the kernel. No new system calls are added for cpusets - all support for querying and modifying cpusets is via this cpuset file system. -The /proc//status file for each task has two added lines, +The /proc//status file for each task has four added lines, displaying the tasks cpus_allowed (on which CPUs it may be scheduled) and mems_allowed (on which Memory Nodes it may obtain memory), -in the format seen in the following example: +in the two formats seen in the following example: Cpus_allowed: ffffffff,ffffffff,ffffffff,ffffffff + Cpus_allowed_list: 0-127 Mems_allowed: ffffffff,ffffffff + Mems_allowed_list: 0-63 Each cpuset is represented by a directory in the cgroup file system containing (on top of the standard cgroup files) the following -- cgit v0.10.2 From 8deacee4f5a64a79a626479ba5d05e5643467513 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 4 Jul 2008 10:00:01 -0700 Subject: MAINTAINERS: update the email address of Andreas Dilger The old one bounces. Signed-off-by: Geert Uytterhoeven Cc: Andreas Dilger Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/MAINTAINERS b/MAINTAINERS index 8c4ad23..25cd6cb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1637,13 +1637,13 @@ S: Maintained EXT3 FILE SYSTEM P: Stephen Tweedie, Andrew Morton -M: sct@redhat.com, akpm@linux-foundation.org, adilger@clusterfs.com +M: sct@redhat.com, akpm@linux-foundation.org, adilger@sun.com L: linux-ext4@vger.kernel.org S: Maintained EXT4 FILE SYSTEM P: Stephen Tweedie, Andrew Morton -M: sct@redhat.com, akpm@linux-foundation.org, adilger@clusterfs.com +M: sct@redhat.com, akpm@linux-foundation.org, adilger@sun.com L: linux-ext4@vger.kernel.org S: Maintained -- cgit v0.10.2 From 491539982aa01fa71de93c2a06ac5d890d4cf1e2 Mon Sep 17 00:00:00 2001 From: Mike Miller Date: Fri, 4 Jul 2008 10:00:02 -0700 Subject: cciss: read config to obtain max outstanding commands per controller This patch changes the way we determine the maximum number of outstanding commands for each controller. Most Smart Array controllers can support up to 1024 commands, the notable exceptions are the E200 and E200i. The next generation of controllers which were just added support a mode of operation called Zero Memory Raid (ZMR). In this mode they only support 64 outstanding commands. In Full Function Raid (FFR) mode they support 1024. We have been setting the queue depth by arbitrarily assigning some value for each controller. We needed a better way to set the queue depth to avoid lots of annoying "fifo full" messages. So we made the driver a little smarter. We now read the config table and subtract 4 from the returned value. The -4 is to allow some room for ioctl calls which are not tracked the same way as io commands are tracked. Please consider this for inclusion. Signed-off-by: Mike Miller Cc: Jens Axboe Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index f552105..d81632c 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -106,35 +106,34 @@ MODULE_DEVICE_TABLE(pci, cciss_pci_device_id); /* board_id = Subsystem Device ID & Vendor ID * product = Marketing Name for the board * access = Address of the struct of function pointers - * nr_cmds = Number of commands supported by controller */ static struct board_type products[] = { - {0x40700E11, "Smart Array 5300", &SA5_access, 512}, - {0x40800E11, "Smart Array 5i", &SA5B_access, 512}, - {0x40820E11, "Smart Array 532", &SA5B_access, 512}, - {0x40830E11, "Smart Array 5312", &SA5B_access, 512}, - {0x409A0E11, "Smart Array 641", &SA5_access, 512}, - {0x409B0E11, "Smart Array 642", &SA5_access, 512}, - {0x409C0E11, "Smart Array 6400", &SA5_access, 512}, - {0x409D0E11, "Smart Array 6400 EM", &SA5_access, 512}, - {0x40910E11, "Smart Array 6i", &SA5_access, 512}, - {0x3225103C, "Smart Array P600", &SA5_access, 512}, - {0x3223103C, "Smart Array P800", &SA5_access, 512}, - {0x3234103C, "Smart Array P400", &SA5_access, 512}, - {0x3235103C, "Smart Array P400i", &SA5_access, 512}, - {0x3211103C, "Smart Array E200i", &SA5_access, 120}, - {0x3212103C, "Smart Array E200", &SA5_access, 120}, - {0x3213103C, "Smart Array E200i", &SA5_access, 120}, - {0x3214103C, "Smart Array E200i", &SA5_access, 120}, - {0x3215103C, "Smart Array E200i", &SA5_access, 120}, - {0x3237103C, "Smart Array E500", &SA5_access, 512}, - {0x323D103C, "Smart Array P700m", &SA5_access, 512}, - {0x3241103C, "Smart Array P212", &SA5_access, 384}, - {0x3243103C, "Smart Array P410", &SA5_access, 384}, - {0x3245103C, "Smart Array P410i", &SA5_access, 384}, - {0x3247103C, "Smart Array P411", &SA5_access, 384}, - {0x3249103C, "Smart Array P812", &SA5_access, 384}, - {0xFFFF103C, "Unknown Smart Array", &SA5_access, 120}, + {0x40700E11, "Smart Array 5300", &SA5_access}, + {0x40800E11, "Smart Array 5i", &SA5B_access}, + {0x40820E11, "Smart Array 532", &SA5B_access}, + {0x40830E11, "Smart Array 5312", &SA5B_access}, + {0x409A0E11, "Smart Array 641", &SA5_access}, + {0x409B0E11, "Smart Array 642", &SA5_access}, + {0x409C0E11, "Smart Array 6400", &SA5_access}, + {0x409D0E11, "Smart Array 6400 EM", &SA5_access}, + {0x40910E11, "Smart Array 6i", &SA5_access}, + {0x3225103C, "Smart Array P600", &SA5_access}, + {0x3223103C, "Smart Array P800", &SA5_access}, + {0x3234103C, "Smart Array P400", &SA5_access}, + {0x3235103C, "Smart Array P400i", &SA5_access}, + {0x3211103C, "Smart Array E200i", &SA5_access}, + {0x3212103C, "Smart Array E200", &SA5_access}, + {0x3213103C, "Smart Array E200i", &SA5_access}, + {0x3214103C, "Smart Array E200i", &SA5_access}, + {0x3215103C, "Smart Array E200i", &SA5_access}, + {0x3237103C, "Smart Array E500", &SA5_access}, + {0x323D103C, "Smart Array P700m", &SA5_access}, + {0x3241103C, "Smart Array P212", &SA5_access}, + {0x3243103C, "Smart Array P410", &SA5_access}, + {0x3245103C, "Smart Array P410i", &SA5_access}, + {0x3247103C, "Smart Array P411", &SA5_access}, + {0x3249103C, "Smart Array P812", &SA5_access}, + {0xFFFF103C, "Unknown Smart Array", &SA5_access}, }; /* How long to wait (in milliseconds) for board to go into simple mode */ @@ -3086,11 +3085,20 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) print_cfg_table(c->cfgtable); #endif /* CCISS_DEBUG */ + /* Some controllers support Zero Memory Raid (ZMR). + * When configured in ZMR mode the number of supported + * commands drops to 64. So instead of just setting an + * arbitrary value we make the driver a little smarter. + * We read the config table to tell us how many commands + * are supported on the controller then subtract 4 to + * leave a little room for ioctl calls. + */ + c->max_commands = readl(&(c->cfgtable->CmdsOutMax)); for (i = 0; i < ARRAY_SIZE(products); i++) { if (board_id == products[i].board_id) { c->product_name = products[i].product_name; c->access = *(products[i].access); - c->nr_cmds = products[i].nr_cmds; + c->nr_cmds = c->max_commands - 4; break; } } @@ -3110,7 +3118,7 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) if (subsystem_vendor_id == PCI_VENDOR_ID_HP) { c->product_name = products[i-1].product_name; c->access = *(products[i-1].access); - c->nr_cmds = products[i-1].nr_cmds; + c->nr_cmds = c->max_commands - 4; printk(KERN_WARNING "cciss: This is an unknown " "Smart Array controller.\n" "cciss: Please update to the latest driver " -- cgit v0.10.2 From e08c1694d9e2138204f2b79b73f0f159074ce2f5 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Fri, 4 Jul 2008 10:00:03 -0700 Subject: olpc: sdhci: add quirk for the Marvell CaFe's vdd/powerup issue This has been sitting around unloved for way too long.. The Marvell CaFe chip's SD implementation chokes during card insertion if one attempts to set the voltage and power up in the same SDHCI_POWER_CONTROL register write. This adds a quirk that does that particular dance in two steps. It also adds an entry to pci_ids.h for the CaFe chip's SD device. Signed-off-by: Andres Salomon Cc: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 07c2048..5b74c8c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -55,6 +55,8 @@ static unsigned int debug_quirks = 0; #define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<7) /* Controller needs to be reset after each request to stay stable */ #define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<8) +/* Controller needs voltage and power writes to happen separately */ +#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<9) static const struct pci_device_id pci_ids[] __devinitdata = { { @@ -128,6 +130,14 @@ static const struct pci_device_id pci_ids[] __devinitdata = { }, { + .vendor = PCI_VENDOR_ID_MARVELL, + .device = PCI_DEVICE_ID_MARVELL_CAFE_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER, + }, + + { .vendor = PCI_VENDOR_ID_JMICRON, .device = PCI_DEVICE_ID_JMICRON_JMB38X_SD, .subvendor = PCI_ANY_ID, @@ -774,6 +784,14 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) BUG(); } + /* + * At least the CaFe chip gets confused if we set the voltage + * and set turn on power at the same time, so set the voltage first. + */ + if ((host->chip->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)) + writeb(pwr & ~SDHCI_POWER_ON, + host->ioaddr + SDHCI_POWER_CONTROL); + writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL); out: diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index eafc9d6..6595382 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1520,6 +1520,7 @@ #define PCI_DEVICE_ID_MARVELL_GT64260 0x6430 #define PCI_DEVICE_ID_MARVELL_MV64360 0x6460 #define PCI_DEVICE_ID_MARVELL_MV64460 0x6480 +#define PCI_DEVICE_ID_MARVELL_CAFE_SD 0x4101 #define PCI_VENDOR_ID_V3 0x11b0 #define PCI_DEVICE_ID_V3_V960 0x0001 -- cgit v0.10.2 From 603ded16a308d0a7a17738c973e3c8cbcd5db7dd Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Fri, 4 Jul 2008 10:00:04 -0700 Subject: olpc: sdhci: add quirk for the Marvell CaFe's interrupt timeout The CaFe chip has a hardware bug that ends up with us getting a timeout value that's too small, causing the following sorts of problems: [ 60.525138] mmcblk0: error -110 transferring data [ 60.531477] end_request: I/O error, dev mmcblk0, sector 1484353 [ 60.533371] Buffer I/O error on device mmcblk0p2, logical block 181632 [ 60.533371] lost page write due to I/O error on mmcblk0p2 Presumably this is an off-by-one error in the hardware. Incrementing the timeout count value that we stuff into the TIMEOUT_CONTROL register gets us a value that works. This bug was originally discovered by Pierre Ossman, I believe. [thanks to Robert Millan for proving that this was still a problem] Signed-off-by: Andres Salomon Cc: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 5b74c8c..2b3f06a 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -57,6 +57,8 @@ static unsigned int debug_quirks = 0; #define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<8) /* Controller needs voltage and power writes to happen separately */ #define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<9) +/* Controller has an off-by-one issue with timeout value */ +#define SDHCI_QUIRK_INCR_TIMEOUT_CONTROL (1<<10) static const struct pci_device_id pci_ids[] __devinitdata = { { @@ -134,7 +136,8 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .device = PCI_DEVICE_ID_MARVELL_CAFE_SD, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER, + .driver_data = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | + SDHCI_QUIRK_INCR_TIMEOUT_CONTROL, }, { @@ -479,6 +482,13 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) break; } + /* + * Compensate for an off-by-one error in the CaFe hardware; otherwise, + * a too-small count gives us interrupt timeouts. + */ + if ((host->chip->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL)) + count++; + if (count >= 0xF) { printk(KERN_WARNING "%s: Too large timeout requested!\n", mmc_hostname(host->mmc)); -- cgit v0.10.2 From acb7669c125676e63cf96582455509216c39745e Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Fri, 4 Jul 2008 10:00:05 -0700 Subject: cpumask: introduce new APIs In linux-next there is a commit ("x86: Add performance variants of cpumask operators") which, as part of the 4096 cpu support work adds some new APIs for dealing with cpu masks. Add trivial versions of these now so that subsystems can update in a timely manner and avoid conflicts in linux-next and the next merge window. Cc: Mike Travis Cc: Thomas Gleixner Cc: Ingo Molnar Signed-off-by: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 5df3db5..c24875b 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -353,6 +353,10 @@ static inline void __cpus_fold(cpumask_t *dstp, const cpumask_t *origp, for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask) #endif /* NR_CPUS */ +#define next_cpu_nr(n, src) next_cpu(n, src) +#define cpus_weight_nr(cpumask) cpus_weight(cpumask) +#define for_each_cpu_mask_nr(cpu, mask) for_each_cpu_mask(cpu, mask) + /* * The following particular system cpumasks and operations manage * possible, present and online cpus. Each of them is a fixed size -- cgit v0.10.2 From 2d5c1be8870383622809c25935fff00d2630c7a5 Mon Sep 17 00:00:00 2001 From: John Blackwood Date: Fri, 4 Jul 2008 10:00:05 -0700 Subject: mm: switch node meminfo Active & Inactive pages to Kbytes There is a bug in the output of /sys/devices/system/node/node[n]/meminfo where the Active and Inactive values are in pages instead of Kbytes. Looks like this occurred back in 2.6.20 when the code was changed over to use node_page_state(). Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/drivers/base/node.c b/drivers/base/node.c index 39f3d1b..0f867a0 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -84,8 +84,8 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) nid, K(i.totalram), nid, K(i.freeram), nid, K(i.totalram - i.freeram), - nid, node_page_state(nid, NR_ACTIVE), - nid, node_page_state(nid, NR_INACTIVE), + nid, K(node_page_state(nid, NR_ACTIVE)), + nid, K(node_page_state(nid, NR_INACTIVE)), #ifdef CONFIG_HIGHMEM nid, K(i.totalhigh), nid, K(i.freehigh), -- cgit v0.10.2 From 26ff8c697a2c8f6974c2357d3f01cca91b20c964 Mon Sep 17 00:00:00 2001 From: Rajiv Andrade Date: Fri, 4 Jul 2008 10:00:06 -0700 Subject: Update MAINTAINERS file for the TPM device driver Acked-By: Debora Velarde Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/MAINTAINERS b/MAINTAINERS index 25cd6cb..6476125 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3997,7 +3997,8 @@ W: http://www.buzzard.org.uk/toshiba/ S: Maintained TPM DEVICE DRIVER -P: Kylene Hall +P: Debora Velarde +P: Rajiv Andrade M: tpmdd-devel@lists.sourceforge.net W: http://tpmdd.sourceforge.net P: Marcel Selhorst -- cgit v0.10.2 From d823f6bfec2844493c05961133895de21fa0e02d Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Fri, 4 Jul 2008 10:00:07 -0700 Subject: devcgroup: fix odd behaviour when writing 'a' to devices.allow # cat /devcg/devices.list a *:* rwm # echo a > devices.allow # cat /devcg/devices.list a *:* rwm a 0:0 rwm This is odd and maybe confusing. With this patch, writing 'a' to devices.allow will add 'a *:* rwm' to the whitelist. Also a few fixes and updates to the document. Signed-off-by: Li Zefan Cc: Pavel Emelyanov Cc: Serge E. Hallyn Cc: Paul Menage Cc: Balbir Singh Cc: James Morris Cc: Chris Wright Cc: Stephen Smalley Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/Documentation/controllers/devices.txt b/Documentation/controllers/devices.txt index 4dcea42..7cc6e6a 100644 --- a/Documentation/controllers/devices.txt +++ b/Documentation/controllers/devices.txt @@ -13,7 +13,7 @@ either an integer or * for all. Access is a composition of r The root device cgroup starts with rwm to 'all'. A child device cgroup gets a copy of the parent. Administrators can then remove devices from the whitelist or add new entries. A child cgroup can -never receive a device access which is denied its parent. However +never receive a device access which is denied by its parent. However when a device access is removed from a parent it will not also be removed from the child(ren). @@ -29,7 +29,11 @@ allows cgroup 1 to read and mknod the device usually known as echo a > /cgroups/1/devices.deny -will remove the default 'a *:* mrw' entry. +will remove the default 'a *:* rwm' entry. Doing + + echo a > /cgroups/1/devices.allow + +will add the 'a *:* rwm' entry to the whitelist. 3. Security diff --git a/security/device_cgroup.c b/security/device_cgroup.c index baf3488..fd764a0 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -382,6 +382,8 @@ static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft, case 'a': wh.type = DEV_ALL; wh.access = ACC_MASK; + wh.major = ~0; + wh.minor = ~0; goto handle; case 'b': wh.type = DEV_BLOCK; -- cgit v0.10.2 From 46b6d94eb04a718730c73b83db889341aad0515e Mon Sep 17 00:00:00 2001 From: Paul Jackson Date: Fri, 4 Jul 2008 10:00:09 -0700 Subject: doc: document the relax_domain_level kernel boot argument Document the kernel boot parameter: relax_domain_level=. Signed-off-by: Paul Jackson Cc: Michael Kerrisk Reviewed-by: Li Zefan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/Documentation/cpusets.txt b/Documentation/cpusets.txt index 894b844..1f5a924 100644 --- a/Documentation/cpusets.txt +++ b/Documentation/cpusets.txt @@ -546,6 +546,9 @@ otherwise initial value -1 that indicates the cpuset has no request. ( 4 : search nodes in a chunk of node [on NUMA system] ) ( 5 : search system wide [on NUMA system] ) +The system default is architecture dependent. The system default +can be changed using the relax_domain_level= boot parameter. + This file is per-cpuset and affect the sched domain where the cpuset belongs to. Therefore if the flag 'sched_load_balance' of a cpuset is disabled, then 'sched_relax_domain_level' have no effect since diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 85c6f57..b52f47d 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1679,6 +1679,10 @@ and is between 256 and 4096 characters. It is defined in the file Format: [,[,...]] See arch/*/kernel/reboot.c or arch/*/kernel/process.c + relax_domain_level= + [KNL, SMP] Set scheduler's default relax_domain_level. + See Documentation/cpusets.txt. + reserve= [KNL,BUGS] Force the kernel to ignore some iomem area reservetop= [X86-32] -- cgit v0.10.2 From bf5b1935d8e42b36a34645788eb261461fe07f2e Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 4 Jul 2008 12:51:20 +0200 Subject: mmc: don't use DMA on newer ENE controllers Even the newer ENE controllers have bugs in their DMA engine that make it too dangerous to use. Disable it until someone has figured out under which conditions it corrupts data. This has caused problems at least once, and can be found as bug report 10925 in the kernel bugzilla. Signed-off-by: Pierre Ossman Signed-off-by: Linus Torvalds diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2b3f06a..b413aa6 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -119,7 +119,8 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE | - SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS, + SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS | + SDHCI_QUIRK_BROKEN_DMA, }, { @@ -128,7 +129,8 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE | - SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS, + SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS | + SDHCI_QUIRK_BROKEN_DMA, }, { -- cgit v0.10.2 From d79df630f622806c4d0e116fbaf6ebf6baf53461 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Fri, 4 Jul 2008 12:24:13 -0700 Subject: mempolicy: mask off internal flags for userspace API Flags considered internal to the mempolicy kernel code are stored as part of the "flags" member of struct mempolicy. Before exposing a policy type to userspace via get_mempolicy(), these internal flags must be masked. Flags exposed to userspace, however, should still be returned to the user. Signed-off-by: David Rientjes Signed-off-by: Linus Torvalds diff --git a/mm/mempolicy.c b/mm/mempolicy.c index a37a503..c94e58b 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -729,7 +729,11 @@ static long do_get_mempolicy(int *policy, nodemask_t *nmask, } else { *policy = pol == &default_policy ? MPOL_DEFAULT : pol->mode; - *policy |= pol->flags; + /* + * Internal mempolicy flags must be masked off before exposing + * the policy to userspace. + */ + *policy |= (pol->flags & MPOL_MODE_FLAGS); } if (vma) { -- cgit v0.10.2 From 4b4f7280d7fd1feeff134c2cf2db32fd583b6c29 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 24 Jun 2008 23:03:48 +0200 Subject: x86 ACPI: normalize segment descriptor register on resume Some Dell laptops enter resume with apparent garbage in the segment descriptor registers (almost certainly the result of a botched transition from protected to real mode.) The only way to clean that up is to enter protected mode ourselves and clean out the descriptor registers. This fixes resume on Dell XPS M1210 and Dell D620. Reference: http://bugzilla.kernel.org/show_bug.cgi?id=10927 Signed-off-by: H. Peter Anvin Cc: Andrew Morton Cc: Pavel Machek Cc: pm list Cc: Len Brown Signed-off-by: Ingo Molnar Tested-by: Kirill A. Shutemov Signed-off-by: Rafael J. Wysocki Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/acpi/realmode/wakeup.S b/arch/x86/kernel/acpi/realmode/wakeup.S index f9b77fb..3355973 100644 --- a/arch/x86/kernel/acpi/realmode/wakeup.S +++ b/arch/x86/kernel/acpi/realmode/wakeup.S @@ -5,6 +5,7 @@ #include #include #include +#include .code16 .section ".header", "a" @@ -24,6 +25,11 @@ pmode_gdt: .quad 0 realmode_flags: .long 0 real_magic: .long 0 trampoline_segment: .word 0 +_pad1: .byte 0 +wakeup_jmp: .byte 0xea /* ljmpw */ +wakeup_jmp_off: .word 3f +wakeup_jmp_seg: .word 0 +wakeup_gdt: .quad 0, 0, 0 signature: .long 0x51ee1111 .text @@ -34,11 +40,34 @@ _start: cli cld + /* Apparently some dimwit BIOS programmers don't know how to + program a PM to RM transition, and we might end up here with + junk in the data segment descriptor registers. The only way + to repair that is to go into PM and fix it ourselves... */ + movw $16, %cx + lgdtl %cs:wakeup_gdt + movl %cr0, %eax + orb $X86_CR0_PE, %al + movl %eax, %cr0 + jmp 1f +1: ljmpw $8, $2f +2: + movw %cx, %ds + movw %cx, %es + movw %cx, %ss + movw %cx, %fs + movw %cx, %gs + + andb $~X86_CR0_PE, %al + movl %eax, %cr0 + jmp wakeup_jmp +3: /* Set up segments */ movw %cs, %ax movw %ax, %ds movw %ax, %es movw %ax, %ss + lidtl wakeup_idt movl $wakeup_stack_end, %esp @@ -98,7 +127,14 @@ bogus_real_magic: jmp 1b .data - .balign 4 + .balign 8 + + /* This is the standard real-mode IDT */ +wakeup_idt: + .word 0xffff /* limit */ + .long 0 /* address */ + .word 0 + .globl HEAP, heap_end HEAP: .long wakeup_heap diff --git a/arch/x86/kernel/acpi/realmode/wakeup.h b/arch/x86/kernel/acpi/realmode/wakeup.h index ef8166f..69d38d0 100644 --- a/arch/x86/kernel/acpi/realmode/wakeup.h +++ b/arch/x86/kernel/acpi/realmode/wakeup.h @@ -24,6 +24,11 @@ struct wakeup_header { u32 realmode_flags; u32 real_magic; u16 trampoline_segment; /* segment with trampoline code, 64-bit only */ + u8 _pad1; + u8 wakeup_jmp; + u16 wakeup_jmp_off; + u16 wakeup_jmp_seg; + u64 wakeup_gdt[3]; u32 signature; /* To check we have correct structure */ } __attribute__((__packed__)); diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c index afc25ee..36af01f 100644 --- a/arch/x86/kernel/acpi/sleep.c +++ b/arch/x86/kernel/acpi/sleep.c @@ -50,6 +50,20 @@ int acpi_save_state_mem(void) header->video_mode = saved_video_mode; + header->wakeup_jmp_seg = acpi_wakeup_address >> 4; + /* GDT[0]: GDT self-pointer */ + header->wakeup_gdt[0] = + (u64)(sizeof(header->wakeup_gdt) - 1) + + ((u64)(acpi_wakeup_address + + ((char *)&header->wakeup_gdt - (char *)acpi_realmode)) + << 16); + /* GDT[1]: real-mode-like code segment */ + header->wakeup_gdt[1] = (0x009bULL << 40) + + ((u64)acpi_wakeup_address << 16) + 0xffff; + /* GDT[2]: real-mode-like data segment */ + header->wakeup_gdt[2] = (0x0093ULL << 40) + + ((u64)acpi_wakeup_address << 16) + 0xffff; + #ifndef CONFIG_64BIT store_gdt((struct desc_ptr *)&header->pmode_gdt); @@ -111,7 +125,7 @@ void __init acpi_reserve_bootmem(void) return; } - acpi_wakeup_address = acpi_realmode; + acpi_wakeup_address = virt_to_phys((void *)acpi_realmode); } diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index c3b0cd8..495c63a 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -36,9 +36,8 @@ static int acpi_sleep_prepare(u32 acpi_state) if (!acpi_wakeup_address) { return -EFAULT; } - acpi_set_firmware_waking_vector((acpi_physical_address) - virt_to_phys((void *) - acpi_wakeup_address)); + acpi_set_firmware_waking_vector( + (acpi_physical_address)acpi_wakeup_address); } ACPI_FLUSH_CPU_CACHE(); -- cgit v0.10.2 From 64e83b5a919a65eb35b63dd7e07c188379ff8ce6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 5 Jul 2008 00:05:30 +0200 Subject: x86 ACPI: fix resume from suspend to RAM on uniprocessor x86-64 Since the trampoline code is now used for ACPI resume from suspend to RAM, the trampoline page tables have to be fixed up during boot not only on SMP systems, but also on UP systems that use the trampoline. Reference: http://bugzilla.kernel.org/show_bug.cgi?id=10923 Reported-by: Dionisus Torimens Signed-off-by: Rafael J. Wysocki Cc: Andi Kleen Cc: Andrew Morton Cc: pm list Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S index 10a1955..b817974 100644 --- a/arch/x86/kernel/head_64.S +++ b/arch/x86/kernel/head_64.S @@ -128,7 +128,7 @@ ident_complete: /* Fixup phys_base */ addq %rbp, phys_base(%rip) -#ifdef CONFIG_SMP +#ifdef CONFIG_X86_TRAMPOLINE addq %rbp, trampoline_level4_pgt + 0(%rip) addq %rbp, trampoline_level4_pgt + (511*8)(%rip) #endif -- cgit v0.10.2 From 3b7253238801a7b97b3929d8db2fa7a0721fb17b Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Mon, 16 Jun 2008 15:51:08 -0700 Subject: softlockup: print a module list on being stuck Most places in the kernel that go BUG: print a module list (which is very useful for doing statistics and finding patterns), however the softlockup detector does not do this yet. This patch adds the one line change to fix this gap. Signed-off-by: Arjan van de Ven Signed-off-by: Ingo Molnar diff --git a/kernel/softlockup.c b/kernel/softlockup.c index c828c23..a272d78 100644 --- a/kernel/softlockup.c +++ b/kernel/softlockup.c @@ -120,6 +120,7 @@ void softlockup_tick(void) printk(KERN_ERR "BUG: soft lockup - CPU#%d stuck for %lus! [%s:%d]\n", this_cpu, now - touch_timestamp, current->comm, task_pid_nr(current)); + print_modules(); if (regs) show_regs(regs); else -- cgit v0.10.2 From 7cd95f56cb61f5348d062527c9d3653196f6e629 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Sat, 5 Jul 2008 20:30:51 +0200 Subject: ide: fix hwif->gendev refcounting class->dev_release is called by device_release() iff dev->release is not present so ide_port_class_release() is never called and the last hwif->gendev reference is not dropped. Fix it by removing ide_port_class_release() and get_device() call from ide_register_port() (device_create_drvdata() takes a hwif->gendev reference anyway). This patch fixes hang on wait_for_completion(&hwif->gendev_rel_comp) in ide_unregister() reported by Pavel Machek. Cc: Pavel Machek Cc: "Rafael J. Wysocki" Cc: Greg KH Signed-off-by: Bartlomiej Zolnierkiewicz diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 380fa0c..d27061b 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -646,8 +646,6 @@ static int ide_register_port(ide_hwif_t *hwif) goto out; } - get_device(&hwif->gendev); - hwif->portdev = device_create_drvdata(ide_port_class, &hwif->gendev, MKDEV(0, 0), hwif, hwif->name); if (IS_ERR(hwif->portdev)) { diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index c758dcb..2460777 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -1094,13 +1094,6 @@ struct bus_type ide_bus_type = { EXPORT_SYMBOL_GPL(ide_bus_type); -static void ide_port_class_release(struct device *portdev) -{ - ide_hwif_t *hwif = dev_get_drvdata(portdev); - - put_device(&hwif->gendev); -} - int ide_vlb_clk; EXPORT_SYMBOL_GPL(ide_vlb_clk); @@ -1305,7 +1298,6 @@ static int __init ide_init(void) ret = PTR_ERR(ide_port_class); goto out_port_class; } - ide_port_class->dev_release = ide_port_class_release; init_ide_data(); -- cgit v0.10.2 From bd8a59e29726b2a5ff7baefe995febdc63044a61 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Sat, 5 Jul 2008 20:30:51 +0200 Subject: ide: ide_unregister() warm-plug bugfix Fix ide_unregister() to work for ports with no devices attached to them. Signed-off-by: Bartlomiej Zolnierkiewicz diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 2460777..b31359c 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -315,13 +315,14 @@ void ide_unregister(ide_hwif_t *hwif) BUG_ON(in_interrupt()); BUG_ON(irqs_disabled()); + mutex_lock(&ide_cfg_mtx); - spin_lock_irq(&ide_lock); - if (!hwif->present) - goto abort; - __ide_port_unregister_devices(hwif); - hwif->present = 0; + spin_lock_irq(&ide_lock); + if (hwif->present) { + __ide_port_unregister_devices(hwif); + hwif->present = 0; + } spin_unlock_irq(&ide_lock); ide_proc_unregister_port(hwif); @@ -359,7 +360,6 @@ void ide_unregister(ide_hwif_t *hwif) /* restore hwif data to pristine status */ ide_init_port_data(hwif, hwif->index); -abort: spin_unlock_irq(&ide_lock); mutex_unlock(&ide_cfg_mtx); } -- cgit v0.10.2 From 2b54ed9467697b0ce2d60d89e5e4253c9e322c26 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Sat, 5 Jul 2008 20:30:51 +0200 Subject: ide: ide_unregister() locking bugfix Holding ide_lock for ide_release_dma_engine() call is unnecessary and triggers WARN_ON(irqs_disabled()) in dma_free_coherent(). Signed-off-by: Bartlomiej Zolnierkiewicz diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index b31359c..300431d 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -352,15 +352,15 @@ void ide_unregister(ide_hwif_t *hwif) blk_unregister_region(MKDEV(hwif->major, 0), MAX_DRIVES<sg_table); unregister_blkdev(hwif->major, hwif->name); - spin_lock_irq(&ide_lock); if (hwif->dma_base) ide_release_dma_engine(hwif); + spin_lock_irq(&ide_lock); /* restore hwif data to pristine status */ ide_init_port_data(hwif, hwif->index); - spin_unlock_irq(&ide_lock); + mutex_unlock(&ide_cfg_mtx); } -- cgit v0.10.2 From d28f87aa87ce8b196349d7c306a7e6fe3abd7155 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 5 Jul 2008 13:10:50 +0900 Subject: ahci: give another shot at clearing all bits in irq_stat Commit ea0c62f7cf70f13a67830471b613337bd0c9a62e tried to clear all bits in irq_stat but it didn't actually achieve that as irq_stat was anded with port_map right after read. This patch makes ahci driver always use the unmasked value to clear irq_status. While at it, add explanation on the peculiarities of ahci IRQ clearing. This was spotted by Linus Torvalds. Signed-off-by: Tejun Heo Signed-off-by: Linus Torvalds diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 061817a..5e6468a 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1777,7 +1777,7 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) struct ahci_host_priv *hpriv; unsigned int i, handled = 0; void __iomem *mmio; - u32 irq_stat; + u32 irq_stat, irq_masked; VPRINTK("ENTER\n"); @@ -1786,16 +1786,17 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) /* sigh. 0xffffffff is a valid return from h/w */ irq_stat = readl(mmio + HOST_IRQ_STAT); - irq_stat &= hpriv->port_map; if (!irq_stat) return IRQ_NONE; + irq_masked = irq_stat & hpriv->port_map; + spin_lock(&host->lock); for (i = 0; i < host->n_ports; i++) { struct ata_port *ap; - if (!(irq_stat & (1 << i))) + if (!(irq_masked & (1 << i))) continue; ap = host->ports[i]; @@ -1812,6 +1813,15 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) handled = 1; } + /* HOST_IRQ_STAT behaves as level triggered latch meaning that + * it should be cleared after all the port events are cleared; + * otherwise, it will raise a spurious interrupt after each + * valid one. Please read section 10.6.2 of ahci 1.1 for more + * information. + * + * Also, use the unmasked value to clear interrupt as spurious + * pending event on a dummy port might cause screaming IRQ. + */ writel(irq_stat, mmio + HOST_IRQ_STAT); spin_unlock(&host->lock); -- cgit v0.10.2 From 20cbc972617069c1ed434f62151e4de57d26ea46 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 5 Jul 2008 12:29:05 -0700 Subject: Fix clear_refs_write() use of struct mm_walk Don't use a static entry, so as to prevent races during concurrent use of this function. Reported-by: Alexey Dobriyan Cc: Matt Mackall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index ab8ccc9..05053d70 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -476,10 +476,10 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, return -ESRCH; mm = get_task_mm(task); if (mm) { - static struct mm_walk clear_refs_walk; - memset(&clear_refs_walk, 0, sizeof(clear_refs_walk)); - clear_refs_walk.pmd_entry = clear_refs_pte_range; - clear_refs_walk.mm = mm; + struct mm_walk clear_refs_walk = { + .pmd_entry = clear_refs_pte_range, + .mm = mm, + }; down_read(&mm->mmap_sem); for (vma = mm->mmap; vma; vma = vma->vm_next) { clear_refs_walk.private = vma; -- cgit v0.10.2 From ca31e146d5c2fe51498e619eb3a64782d02e310a Mon Sep 17 00:00:00 2001 From: Eduard - Gabriel Munteanu Date: Sat, 5 Jul 2008 12:14:23 +0300 Subject: Move _RET_IP_ and _THIS_IP_ to include/linux/kernel.h These two macros are useful beyond lock debugging. Moved definitions from include/linux/debug_locks.h to include/linux/kernel.h, so code that needs them does not have to include the former, which would have been a less intuitive choice of a header. Signed-off-by: Eduard - Gabriel Munteanu Acked-by: Pekka Enberg Signed-off-by: Linus Torvalds diff --git a/include/linux/debug_locks.h b/include/linux/debug_locks.h index f4a5871..4aaa4af 100644 --- a/include/linux/debug_locks.h +++ b/include/linux/debug_locks.h @@ -1,6 +1,8 @@ #ifndef __LINUX_DEBUG_LOCKING_H #define __LINUX_DEBUG_LOCKING_H +#include + struct task_struct; extern int debug_locks; @@ -11,14 +13,6 @@ extern int debug_locks_silent; */ extern int debug_locks_off(void); -/* - * In the debug case we carry the caller's instruction pointer into - * other functions, but we dont want the function argument overhead - * in the nondebug case - hence these macros: - */ -#define _RET_IP_ (unsigned long)__builtin_return_address(0) -#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) - #define DEBUG_LOCKS_WARN_ON(c) \ ({ \ int __ret = 0; \ diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 792bf0a..2e70006 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -46,6 +46,9 @@ extern const char linux_proc_banner[]; #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) #define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) +#define _RET_IP_ (unsigned long)__builtin_return_address(0) +#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) + #ifdef CONFIG_LBD # include # define sector_div(a, b) do_div(a, b) -- cgit v0.10.2 From 5d7e0d2bd98ef4f5a16ac9da1987ae655368dd6a Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 5 Jul 2008 01:02:01 -0700 Subject: Fix pagemap_read() use of struct mm_walk Fix some issues in pagemap_read noted by Alexey: - initialize pagemap_walk.mm to "mm" , so the code starts working as advertised - initialize ->private to "&pm" so it wouldn't immediately oops in pagemap_pte_hole() - unstatic struct pagemap_walk, so two threads won't fsckup each other (including those started by root, including flipping ->mm when you don't have permissions) - pagemap_read() contains two calls to ptrace_may_attach(), second one looks unneeded. - avoid possible kmalloc(0) and integer wraparound. Cc: Alexey Dobriyan Cc: Matt Mackall Signed-off-by: Andrew Morton [ Personally, I'd just remove the functionality entirely - Linus ] Signed-off-by: Linus Torvalds diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 05053d70..c492449 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -602,11 +602,6 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, return err; } -static struct mm_walk pagemap_walk = { - .pmd_entry = pagemap_pte_range, - .pte_hole = pagemap_pte_hole -}; - /* * /proc/pid/pagemap - an array mapping virtual pages to pfns * @@ -641,6 +636,11 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, struct pagemapread pm; int pagecount; int ret = -ESRCH; + struct mm_walk pagemap_walk; + unsigned long src; + unsigned long svpfn; + unsigned long start_vaddr; + unsigned long end_vaddr; if (!task) goto out; @@ -659,11 +659,15 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, if (!mm) goto out_task; - ret = -ENOMEM; + uaddr = (unsigned long)buf & PAGE_MASK; uend = (unsigned long)(buf + count); pagecount = (PAGE_ALIGN(uend) - uaddr) / PAGE_SIZE; - pages = kmalloc(pagecount * sizeof(struct page *), GFP_KERNEL); + ret = 0; + if (pagecount == 0) + goto out_mm; + pages = kcalloc(pagecount, sizeof(struct page *), GFP_KERNEL); + ret = -ENOMEM; if (!pages) goto out_mm; @@ -684,33 +688,33 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, pm.out = (u64 *)buf; pm.end = (u64 *)(buf + count); - if (!ptrace_may_attach(task)) { - ret = -EIO; - } else { - unsigned long src = *ppos; - unsigned long svpfn = src / PM_ENTRY_BYTES; - unsigned long start_vaddr = svpfn << PAGE_SHIFT; - unsigned long end_vaddr = TASK_SIZE_OF(task); - - /* watch out for wraparound */ - if (svpfn > TASK_SIZE_OF(task) >> PAGE_SHIFT) - start_vaddr = end_vaddr; - - /* - * The odds are that this will stop walking way - * before end_vaddr, because the length of the - * user buffer is tracked in "pm", and the walk - * will stop when we hit the end of the buffer. - */ - ret = walk_page_range(start_vaddr, end_vaddr, - &pagemap_walk); - if (ret == PM_END_OF_BUFFER) - ret = 0; - /* don't need mmap_sem for these, but this looks cleaner */ - *ppos += (char *)pm.out - buf; - if (!ret) - ret = (char *)pm.out - buf; - } + pagemap_walk.pmd_entry = pagemap_pte_range; + pagemap_walk.pte_hole = pagemap_pte_hole; + pagemap_walk.mm = mm; + pagemap_walk.private = ± + + src = *ppos; + svpfn = src / PM_ENTRY_BYTES; + start_vaddr = svpfn << PAGE_SHIFT; + end_vaddr = TASK_SIZE_OF(task); + + /* watch out for wraparound */ + if (svpfn > TASK_SIZE_OF(task) >> PAGE_SHIFT) + start_vaddr = end_vaddr; + + /* + * The odds are that this will stop walking way + * before end_vaddr, because the length of the + * user buffer is tracked in "pm", and the walk + * will stop when we hit the end of the buffer. + */ + ret = walk_page_range(start_vaddr, end_vaddr, &pagemap_walk); + if (ret == PM_END_OF_BUFFER) + ret = 0; + /* don't need mmap_sem for these, but this looks cleaner */ + *ppos += (char *)pm.out - buf; + if (!ret) + ret = (char *)pm.out - buf; out_pages: for (; pagecount; pagecount--) { -- cgit v0.10.2 From b7279469d66b55119784b8b9529c99c1955fe747 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 5 Jul 2008 15:53:22 -0700 Subject: Linux 2.6.26-rc9 diff --git a/Makefile b/Makefile index 6aff5f4..6315424 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 26 -EXTRAVERSION = -rc8 +EXTRAVERSION = -rc9 NAME = Rotary Wombat # *DOCUMENTATION* -- cgit v0.10.2